module Network.WebSockets.Extensions.Description
( ExtensionParam
, ExtensionDescription (..)
, ExtensionDescriptions
, parseExtensionDescriptions
, encodeExtensionDescriptions
) where
import Control.Applicative ((*>), (<*))
import qualified Data.Attoparsec.ByteString as A
import qualified Data.Attoparsec.ByteString.Char8 as AC8
import qualified Data.ByteString as B
import Data.Monoid (mconcat, mappend)
import Prelude
type ExtensionParam = (B.ByteString, Maybe B.ByteString)
data ExtensionDescription = ExtensionDescription
{ extName :: !B.ByteString
, extParams :: ![ExtensionParam]
} deriving (Eq, Show)
parseExtensionDescription :: A.Parser ExtensionDescription
parseExtensionDescription = do
extName <- parseIdentifier
extParams <- A.many' (token ';' *> parseParam)
return ExtensionDescription {..}
where
parseIdentifier = AC8.takeWhile isIdentifierChar <* AC8.skipSpace
token c = AC8.char8 c <* AC8.skipSpace
isIdentifierChar c =
(c >= 'a' && c <= 'z') ||
(c >= 'A' && c <= 'Z') ||
(c >= '0' && c <= '9') ||
c == '-' || c == '_'
parseParam :: A.Parser ExtensionParam
parseParam = do
name <- parseIdentifier
val <- A.option Nothing $ fmap Just $ token '=' *> parseIdentifier
return (name, val)
encodeExtensionDescription :: ExtensionDescription -> B.ByteString
encodeExtensionDescription ExtensionDescription {..} =
mconcat (extName : map encodeParam extParams)
where
encodeParam (key, Nothing) = ";" `mappend` key
encodeParam (key, Just val) = ";" `mappend` key `mappend` "=" `mappend` val
type ExtensionDescriptions = [ExtensionDescription]
parseExtensionDescriptions :: B.ByteString -> Either String ExtensionDescriptions
parseExtensionDescriptions = A.parseOnly $
AC8.skipSpace *>
A.sepBy parseExtensionDescription (AC8.char8 ',' <* AC8.skipSpace) <*
A.endOfInput
encodeExtensionDescriptions :: ExtensionDescriptions -> B.ByteString
encodeExtensionDescriptions = B.intercalate "," . map encodeExtensionDescription