{-# LANGUAGE OverloadedStrings, UnicodeSyntax, Safe, CPP #-}
{-# OPTIONS_GHC -fno-warn-unused-do-bind #-}
module Network.HTTP.Link.Parser (
linkHeader
, parseLinkHeader'
, parseLinkHeader
, parseLinkHeaderBS'
, parseLinkHeaderBS
, parseGLinkHeader'
, parseGLinkHeader
, parseGLinkHeaderBS'
, parseGLinkHeaderBS
) where
import Prelude hiding (takeWhile, take)
import Control.Applicative
import Control.Error.Util (hush)
import Data.Text hiding (takeWhile, map, take)
import Data.Text.Encoding (decodeUtf8)
import Data.ByteString (ByteString)
import Data.Char (isSpace)
#if !MIN_VERSION_base(4,8,0)
import Data.Monoid (mconcat)
#endif
import Data.Attoparsec.Text
import Network.URI
import Network.HTTP.Link.Types
allConditions ∷ [a → Bool] → a → Bool
allConditions cs x = and $ map ($ x) cs
charWS ∷ Char → Parser ()
charWS x = skipSpace >> char x >> skipSpace
quotedString ∷ Parser Text
quotedString = do
char '"'
v ← many stringPart
char '"'
return $ pack $ unEscapeString $ unpack $ mconcat v
where stringPart = takeWhile1 (allConditions [(/= '"'), (/= '\\')]) <|> escapedChar
escapedChar = char '\\' >> take 1
paramName ∷ Text → LinkParam
paramName "rel" = Rel
paramName "anchor" = Anchor
paramName "rev" = Rev
paramName "hreflang" = Hreflang
paramName "media" = Media
paramName "title" = Title
paramName "title*" = Title'
paramName "type" = ContentType
paramName x = Other x
relType ∷ Parser Text
relType = takeWhile1 $ inClass "-0-9a-z."
paramValue ∷ LinkParam → Parser Text
paramValue Rel = quotedString <|> relType
paramValue Rev = quotedString <|> relType
paramValue Title' = takeWhile (allConditions [not . isSpace])
paramValue _ = quotedString
param ∷ Parser (LinkParam, Text)
param = do
charWS ';'
n ← takeWhile (allConditions [(/= '='), not . isSpace])
let n' = paramName n
charWS '='
v ← paramValue n'
return (n', v)
glink ∷ (IsURI uri) ⇒ Parser (GLink uri)
glink = do
charWS '<'
linkText ← takeWhile1 $ allConditions [(/= '>'), not . isSpace]
charWS '>'
params ← many' $ param
skipSpace
case uriFromText linkText of
Right u → return $ Link u params
Left e → fail $ "Couldn't parse the URI " ++ show linkText ++ if e == "" then "" else ": " ++ e
glinkHeader ∷ (IsURI uri) ⇒ Parser [GLink uri]
glinkHeader = glink `sepBy'` (char ',')
linkHeader ∷ Parser [Link]
linkHeader = glinkHeader
parseGLinkHeader' ∷ (IsURI uri) ⇒ Text → Either String [GLink uri]
parseGLinkHeader' = parseOnly glinkHeader
parseGLinkHeader ∷ (IsURI uri) ⇒ Text → Maybe [GLink uri]
parseGLinkHeader = hush . parseGLinkHeader'
parseGLinkHeaderBS' ∷ (IsURI uri) ⇒ ByteString → Either String [GLink uri]
parseGLinkHeaderBS' = parseGLinkHeader' . decodeUtf8
parseGLinkHeaderBS ∷ (IsURI uri) ⇒ ByteString → Maybe [GLink uri]
parseGLinkHeaderBS = parseGLinkHeader . decodeUtf8
parseLinkHeader' ∷ Text → Either String [Link]
parseLinkHeader' = parseGLinkHeader'
parseLinkHeader ∷ Text → Maybe [Link]
parseLinkHeader = parseGLinkHeader
parseLinkHeaderBS' ∷ ByteString → Either String [Link]
parseLinkHeaderBS' = parseGLinkHeaderBS'
parseLinkHeaderBS ∷ ByteString → Maybe [Link]
parseLinkHeaderBS = parseGLinkHeaderBS