-- | Functions related to parsing @Cache-Control@ header as defined in
-- https://tools.ietf.org/html/rfc7234#section-5.2
--
-- To get @max-age@/@s-maxage@ from @Cache-Control@ header, use 'parseMaxAge'. If you need to check
-- other directives use 'parseCacheControl'.
--
-- Rules which starts with @obs-@ is not required to implement because they are maked as "obsolete" as
-- per https://tools.ietf.org/html/rfc7230#section-1.2
module Data.Parser.CacheControl
  ( CacheControl,
    CacheControlDirective (..),
    parseCacheControl,
    parseMaxAge,
    findMaxAge,
    noCacheExists,
    noStoreExists,
    mustRevalidateExists,
  )
where

import Data.Attoparsec.Text qualified as AT
import Data.Text qualified as T
import Hasura.Prelude
import Hasura.Server.Utils (fmapL)

type CacheControl = [CacheControlDirective]

data CacheControlDirective
  = CCDOnlyToken !Text
  | CCDTokenWithVal !Text !Text
  deriving (Int -> CacheControlDirective -> ShowS
[CacheControlDirective] -> ShowS
CacheControlDirective -> String
(Int -> CacheControlDirective -> ShowS)
-> (CacheControlDirective -> String)
-> ([CacheControlDirective] -> ShowS)
-> Show CacheControlDirective
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [CacheControlDirective] -> ShowS
$cshowList :: [CacheControlDirective] -> ShowS
show :: CacheControlDirective -> String
$cshow :: CacheControlDirective -> String
showsPrec :: Int -> CacheControlDirective -> ShowS
$cshowsPrec :: Int -> CacheControlDirective -> ShowS
Show, CacheControlDirective -> CacheControlDirective -> Bool
(CacheControlDirective -> CacheControlDirective -> Bool)
-> (CacheControlDirective -> CacheControlDirective -> Bool)
-> Eq CacheControlDirective
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: CacheControlDirective -> CacheControlDirective -> Bool
$c/= :: CacheControlDirective -> CacheControlDirective -> Bool
== :: CacheControlDirective -> CacheControlDirective -> Bool
$c== :: CacheControlDirective -> CacheControlDirective -> Bool
Eq)

-- | Tries to parse the @max-age@ or @s-maxage@ present in the value of @Cache-Control@ header
parseMaxAge :: Integral a => Text -> Either String a
parseMaxAge :: Text -> Either String a
parseMaxAge Text
t =
  Text -> Either String [CacheControlDirective]
parseCacheControl Text
t Either String [CacheControlDirective]
-> ([CacheControlDirective] -> Either String (Maybe a))
-> Either String (Maybe a)
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [CacheControlDirective] -> Either String (Maybe a)
forall a.
Integral a =>
[CacheControlDirective] -> Either String (Maybe a)
findMaxAge Either String (Maybe a)
-> (Maybe a -> Either String a) -> Either String a
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Either String a
-> (a -> Either String a) -> Maybe a -> Either String a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (String -> Either String a
forall a b. a -> Either a b
Left String
notFoundErr) a -> Either String a
forall a b. b -> Either a b
Right
  where
    notFoundErr :: String
notFoundErr = String
"could not find max-age/s-maxage"

findMaxAge :: Integral a => CacheControl -> Either String (Maybe a)
findMaxAge :: [CacheControlDirective] -> Either String (Maybe a)
findMaxAge [CacheControlDirective]
cacheControl = do
  case (Text -> Bool) -> [CacheControlDirective] -> Maybe (Text, Text)
findCCDTokenWithVal Text -> Bool
forall a. (Eq a, IsString a) => a -> Bool
checkMaxAgeToken [CacheControlDirective]
cacheControl of
    Just (Text
_, Text
val) -> a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> Either String a -> Either String (Maybe a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ShowS -> Either String a -> Either String a
forall a a' b. (a -> a') -> Either a b -> Either a' b
fmapL ShowS
forall p p. IsString p => p -> p
parseErr (Parser a -> Text -> Either String a
forall a. Parser a -> Text -> Either String a
AT.parseOnly Parser a
forall a. Integral a => Parser a
AT.decimal Text
val)
    Maybe (Text, Text)
Nothing -> Maybe a -> Either String (Maybe a)
forall a b. b -> Either a b
Right Maybe a
forall a. Maybe a
Nothing
  where
    parseErr :: p -> p
parseErr p
_ = p
"could not parse max-age/s-maxage value"
    checkMaxAgeToken :: a -> Bool
checkMaxAgeToken a
token = a
token a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
"max-age" Bool -> Bool -> Bool
|| a
token a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
"s-maxage"

-- | Checks if the @no-cache@ directive is present
noCacheExists :: CacheControl -> Bool
noCacheExists :: [CacheControlDirective] -> Bool
noCacheExists [CacheControlDirective]
cacheControl =
  Maybe Text -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Text -> Bool) -> Maybe Text -> Bool
forall a b. (a -> b) -> a -> b
$ (Text -> Bool) -> [CacheControlDirective] -> Maybe Text
findCCDOnlyToken (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"no-cache") [CacheControlDirective]
cacheControl

-- | Checks if the @no-store@ directive is present
noStoreExists :: CacheControl -> Bool
noStoreExists :: [CacheControlDirective] -> Bool
noStoreExists [CacheControlDirective]
cacheControl =
  Maybe Text -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Text -> Bool) -> Maybe Text -> Bool
forall a b. (a -> b) -> a -> b
$ (Text -> Bool) -> [CacheControlDirective] -> Maybe Text
findCCDOnlyToken (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"no-store") [CacheControlDirective]
cacheControl

-- | Checks if the @must-revalidate@ directive is present
mustRevalidateExists :: CacheControl -> Bool
mustRevalidateExists :: [CacheControlDirective] -> Bool
mustRevalidateExists [CacheControlDirective]
cacheControl =
  Maybe Text -> Bool
forall a. Maybe a -> Bool
isJust (Maybe Text -> Bool) -> Maybe Text -> Bool
forall a b. (a -> b) -> a -> b
$ (Text -> Bool) -> [CacheControlDirective] -> Maybe Text
findCCDOnlyToken (Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"must-revalidate") [CacheControlDirective]
cacheControl

findCCDOnlyToken :: (Text -> Bool) -> CacheControl -> Maybe Text
findCCDOnlyToken :: (Text -> Bool) -> [CacheControlDirective] -> Maybe Text
findCCDOnlyToken Text -> Bool
tokenPredicate [CacheControlDirective]
cacheControl =
  [Text] -> Maybe Text
forall a. [a] -> Maybe a
listToMaybe ([Text] -> Maybe Text) -> [Text] -> Maybe Text
forall a b. (a -> b) -> a -> b
$ (CacheControlDirective -> Maybe Text)
-> [CacheControlDirective] -> [Text]
forall (f :: * -> *) a b.
Filterable f =>
(a -> Maybe b) -> f a -> f b
mapMaybe CacheControlDirective -> Maybe Text
check [CacheControlDirective]
cacheControl
  where
    check :: CacheControlDirective -> Maybe Text
check = \case
      CCDOnlyToken Text
token
        | Text -> Bool
tokenPredicate Text
token -> Text -> Maybe Text
forall a. a -> Maybe a
Just Text
token
        | Bool
otherwise -> Maybe Text
forall a. Maybe a
Nothing
      CCDTokenWithVal Text
_ Text
_ -> Maybe Text
forall a. Maybe a
Nothing

findCCDTokenWithVal :: (Text -> Bool) -> CacheControl -> Maybe (Text, Text)
findCCDTokenWithVal :: (Text -> Bool) -> [CacheControlDirective] -> Maybe (Text, Text)
findCCDTokenWithVal Text -> Bool
tokenPredicate [CacheControlDirective]
cacheControl =
  [(Text, Text)] -> Maybe (Text, Text)
forall a. [a] -> Maybe a
listToMaybe ([(Text, Text)] -> Maybe (Text, Text))
-> [(Text, Text)] -> Maybe (Text, Text)
forall a b. (a -> b) -> a -> b
$ (CacheControlDirective -> Maybe (Text, Text))
-> [CacheControlDirective] -> [(Text, Text)]
forall (f :: * -> *) a b.
Filterable f =>
(a -> Maybe b) -> f a -> f b
mapMaybe CacheControlDirective -> Maybe (Text, Text)
check [CacheControlDirective]
cacheControl
  where
    check :: CacheControlDirective -> Maybe (Text, Text)
check = \case
      CCDOnlyToken Text
_ -> Maybe (Text, Text)
forall a. Maybe a
Nothing
      CCDTokenWithVal Text
token Text
value
        | Text -> Bool
tokenPredicate Text
token -> (Text, Text) -> Maybe (Text, Text)
forall a. a -> Maybe a
Just (Text
token, Text
value)
        | Bool
otherwise -> Maybe (Text, Text)
forall a. Maybe a
Nothing

-- | Parses a @Cache-Control@ header and returns a list of directives
parseCacheControl :: Text -> Either String CacheControl
parseCacheControl :: Text -> Either String [CacheControlDirective]
parseCacheControl = Parser [CacheControlDirective]
-> Text -> Either String [CacheControlDirective]
forall a. Parser a -> Text -> Either String a
AT.parseOnly Parser [CacheControlDirective]
cacheControlParser

-- ABNF: cache-control = *( "," OWS) cache-directive *( OWS "," [OWS cache-directive])
-- https://tools.ietf.org/html/rfc7234#appendix-C
cacheControlParser :: AT.Parser CacheControl
cacheControlParser :: Parser [CacheControlDirective]
cacheControlParser = do
  Parser Text [Maybe Char] -> Parser Text ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Parser Text [Maybe Char] -> Parser Text ())
-> Parser Text [Maybe Char] -> Parser Text ()
forall a b. (a -> b) -> a -> b
$ Parser Text (Maybe Char) -> Parser Text [Maybe Char]
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
AT.many' (Parser Text Text
"," Parser Text Text
-> Parser Text (Maybe Char) -> Parser Text (Maybe Char)
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Text (Maybe Char)
optionalWhitespaceParser)
  CacheControlDirective
cd <- Parser CacheControlDirective
cacheDirectiveParser
  [Maybe CacheControlDirective]
cds <- Parser Text (Maybe CacheControlDirective)
-> Parser Text [Maybe CacheControlDirective]
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
AT.many' (Parser Text (Maybe CacheControlDirective)
 -> Parser Text [Maybe CacheControlDirective])
-> Parser Text (Maybe CacheControlDirective)
-> Parser Text [Maybe CacheControlDirective]
forall a b. (a -> b) -> a -> b
$ Parser Text (Maybe Char)
optionalWhitespaceParser Parser Text (Maybe Char) -> Parser Text Text -> Parser Text Text
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser Text Text
"," Parser Text Text
-> Parser Text (Maybe CacheControlDirective)
-> Parser Text (Maybe CacheControlDirective)
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Maybe CacheControlDirective
-> Parser Text (Maybe CacheControlDirective)
-> Parser Text (Maybe CacheControlDirective)
forall (f :: * -> *) a. Alternative f => a -> f a -> f a
AT.option Maybe CacheControlDirective
forall a. Maybe a
Nothing (CacheControlDirective -> Maybe CacheControlDirective
forall (f :: * -> *) a. Applicative f => a -> f a
pure (CacheControlDirective -> Maybe CacheControlDirective)
-> Parser CacheControlDirective
-> Parser Text (Maybe CacheControlDirective)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser CacheControlDirective
optionalDirective)
  [CacheControlDirective] -> Parser [CacheControlDirective]
forall (m :: * -> *) a. Monad m => a -> m a
return ([CacheControlDirective] -> Parser [CacheControlDirective])
-> [CacheControlDirective] -> Parser [CacheControlDirective]
forall a b. (a -> b) -> a -> b
$ CacheControlDirective
cd CacheControlDirective
-> [CacheControlDirective] -> [CacheControlDirective]
forall a. a -> [a] -> [a]
: [Maybe CacheControlDirective] -> [CacheControlDirective]
forall (f :: * -> *) a. Filterable f => f (Maybe a) -> f a
catMaybes [Maybe CacheControlDirective]
cds
  where
    optionalDirective :: Parser CacheControlDirective
optionalDirective = Parser Text (Maybe Char)
optionalWhitespaceParser Parser Text (Maybe Char)
-> Parser CacheControlDirective -> Parser CacheControlDirective
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> Parser CacheControlDirective
cacheDirectiveParser

-- ABNF: OWS = *( SP / HTAB ) ; optional whitespace
-- https://tools.ietf.org/html/rfc7230#section-3.2.3
optionalWhitespaceParser :: AT.Parser (Maybe Char)
optionalWhitespaceParser :: Parser Text (Maybe Char)
optionalWhitespaceParser = Maybe Char -> Parser Text (Maybe Char) -> Parser Text (Maybe Char)
forall (f :: * -> *) a. Alternative f => a -> f a -> f a
AT.option Maybe Char
forall a. Maybe a
Nothing (Char -> Maybe Char
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Char -> Maybe Char)
-> Parser Text Char -> Parser Text (Maybe Char)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Text Char
AT.space)

-- ABNF: cache-directive = token [ "=" ( token / quoted-string ) ]
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
cacheDirectiveParser :: AT.Parser CacheControlDirective
cacheDirectiveParser :: Parser CacheControlDirective
cacheDirectiveParser = Parser CacheControlDirective
tokenWithValue Parser CacheControlDirective
-> Parser CacheControlDirective -> Parser CacheControlDirective
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser CacheControlDirective
onlyToken
  where
    onlyToken :: Parser CacheControlDirective
onlyToken = Text -> CacheControlDirective
CCDOnlyToken (Text -> CacheControlDirective)
-> Parser Text Text -> Parser CacheControlDirective
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Text Text
tokenParser
    tokenWithValue :: Parser CacheControlDirective
tokenWithValue = do
      Text
tok <- Parser Text Text
tokenParser
      Parser Text Char -> Parser Text ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (Parser Text Char -> Parser Text ())
-> Parser Text Char -> Parser Text ()
forall a b. (a -> b) -> a -> b
$ Char -> Parser Text Char
AT.char Char
'='
      Text
val <- Parser Text Text
tokenParser Parser Text Text -> Parser Text Text -> Parser Text Text
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Text
quotedStringParser
      CacheControlDirective -> Parser CacheControlDirective
forall (m :: * -> *) a. Monad m => a -> m a
return (CacheControlDirective -> Parser CacheControlDirective)
-> CacheControlDirective -> Parser CacheControlDirective
forall a b. (a -> b) -> a -> b
$ Text -> Text -> CacheControlDirective
CCDTokenWithVal Text
tok Text
val

-- ABNF: 1*tchar
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
tokenParser :: AT.Parser Text
tokenParser :: Parser Text Text
tokenParser = String -> Text
T.pack (String -> Text) -> Parser Text String -> Parser Text Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Text Char -> Parser Text String
forall (f :: * -> *) a. Alternative f => f a -> f [a]
AT.many1 Parser Text Char
tcharParser

-- ABNF: tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" / "+" / "-" / "." / "^" / "_" / "`" / "|"
--               / "~" / DIGIT / ALPHA ; any VCHAR, except delimiters
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
tcharParser :: AT.Parser Char
tcharParser :: Parser Text Char
tcharParser =
  Char -> Parser Text Char
AT.char Char
'!'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'#'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'$'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'%'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'&'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'\''
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'*'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'+'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'-'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'.'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'^'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'_'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'`'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'|'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'~'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Char
AT.digit
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Char
AT.letter

-- ABNF: DQUOTE = %x22 ; " (Double Quote)
-- https://tools.ietf.org/html/rfc5234#appendix-B.1
dquoteParser :: AT.Parser Char
dquoteParser :: Parser Text Char
dquoteParser = Char -> Parser Text Char
AT.char Char
'"'

-- ABNF: VCHAR = %x21-7E ; visible (printing) characters
-- https://tools.ietf.org/html/rfc5234#appendix-B.1
vcharParser :: AT.Parser Char
vcharParser :: Parser Text Char
vcharParser = Parser Text Char
AT.anyChar

-- ABNF: quoted-string = DQUOTE *( qdtext / quoted-pair ) DQUOTE
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
quotedStringParser :: AT.Parser Text
quotedStringParser :: Parser Text Text
quotedStringParser =
  Parser Text Char
dquoteParser Parser Text Char -> Parser Text Text -> Parser Text Text
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (String -> Text) -> Parser Text String -> Parser Text Text
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap String -> Text
T.pack (Parser Text Char -> Parser Text String
forall (m :: * -> *) a. MonadPlus m => m a -> m [a]
AT.many' (Parser Text Char
qdTextParser Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Char
quotedPairParser)) Parser Text Text -> Parser Text Char -> Parser Text Text
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f a
<* Parser Text Char
dquoteParser

-- ABNF: quoted-pair = "\" ( HTAB / SP / VCHAR / obs-text )
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
quotedPairParser :: AT.Parser Char
quotedPairParser :: Parser Text Char
quotedPairParser =
  Text -> Parser Text Text
AT.string Text
"\\" Parser Text Text -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a b. Applicative f => f a -> f b -> f b
*> (Parser Text Char
AT.space Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Char
vcharParser)

-- ABNF: qdtext = HTAB / SP / %x21 / %x23-5B / %x5D-7E / obs-text
-- https://tools.ietf.org/html/rfc7230#section-3.2.6
qdTextParser :: AT.Parser Char
qdTextParser :: Parser Text Char
qdTextParser =
  Parser Text Char
AT.space
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'!' -- %x21
    -- skip %x22 as it is '"'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'#' -- %x23
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'$' -- %x24
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'%' -- %x25
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'&' -- %x26
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'\'' -- %x27 single quote
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'(' -- %x28
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
')' -- %x29
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'*' -- %x2A
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'+' -- %x2B
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
',' -- %x2C
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'-' -- %x2D
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'.' -- %x2E
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'/' -- %x2F
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Char
AT.digit -- %x30-39
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
':' -- %x3A
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
';' -- %x3B
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'<' -- %x3C
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'=' -- %x3D
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'>' -- %x3E
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'?' -- %x3F
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'@' -- %x40
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Text Char
AT.letter -- %x41-5A / %x61-7A
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'[' -- %x5B
    -- skip %x5C as it is '\'
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
']' -- %x5D
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'^' -- %x5E
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'_' -- %x5F
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'`' -- %x60
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'{' -- %x7B
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'|' -- %x7C
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'}' -- %x7D
    Parser Text Char -> Parser Text Char -> Parser Text Char
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Char -> Parser Text Char
AT.char Char
'~' -- %x7E