1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
|
{-# LANGUAGE OverloadedStrings #-}
module Network.Wreq.Internal.Link
(
links
) where
import Control.Applicative ((<$>), (<*>), (*>), (<*), many)
import Data.Attoparsec.ByteString.Char8 as A8
import Data.ByteString (ByteString)
import Network.Wreq.Types (Link(..))
import qualified Data.Attoparsec.ByteString as A
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as B8
links :: B.ByteString -> [Link]
links hdr = case parseOnly f hdr of
Left _ -> []
Right xs -> xs
where f = sepBy1 (link <* skipSpace) (char8 ',' *> skipSpace) <* endOfInput
link :: Parser Link
link = Link <$> url <*> many (char8 ';' *> skipSpace *> param)
where url = char8 '<' *> A8.takeTill (=='>') <* char8 '>' <* skipSpace
param :: Parser (ByteString, ByteString)
param = do
name <- paramName
skipSpace *> "=" *> skipSpace
c <- peekChar'
let isTokenChar = A.inClass "!#$%&'()*+./0-9:<=>?@a-zA-Z[]^_`{|}~-"
val <- case c of
'"' -> quotedString
_ -> A.takeWhile isTokenChar
skipSpace
return (name, val)
data Quot = Literal | Backslash
quotedString :: Parser ByteString
quotedString = char '"' *> (fixup <$> body) <* char '"'
where body = A8.scan Literal $ \s c ->
case (s,c) of
(Literal, '\\') -> backslash
(Literal, '"') -> Nothing
_ -> literal
literal = Just Literal
backslash = Just Backslash
fixup = B8.pack . go . B8.unpack
where go ('\\' : x@'\\' : xs) = x : go xs
go ('\\' : x@'"' : xs) = x : go xs
go (x : xs) = x : go xs
go xs = xs
paramName :: Parser ByteString
paramName = do
name <- A.takeWhile1 $ A.inClass "a-zA-Z0-9!#$&+-.^_`|~"
c <- peekChar
return $ case c of
Just '*' -> B8.snoc name '*'
_ -> name
|