network-anonymous-tor-0.9.0: src/Network/Anonymous/Tor/Protocol/Parser/Ast.hs
-- | Abstract syntax tree used by the 'Parser', including helper functions
-- for traversing the tree.
--
-- __Warning__: This function is used internally by 'Network.Anonymous.Tor'
-- and using these functions directly is unsupported. The
-- interface of these functions might change at any time without
-- prior notice.
--
module Network.Anonymous.Tor.Protocol.Parser.Ast where
import qualified Data.Attoparsec.ByteString as Atto
import qualified Data.ByteString as BS
-- | A token is a key and can maybe have an associated value
data Token = Token {
tokenKey :: BS.ByteString,
tokenValue :: Maybe BS.ByteString
} deriving (Show, Eq)
-- | A line is just a sequence of tokens -- the 'Parser' ends the chain
-- when a newline is received.
data Line = Line {
lineStatusCode :: Integer,
lineMessage :: [Token]
} deriving (Show, Eq)
-- | Returns true if the key was found
key :: BS.ByteString -- ^ The key to look for
-> [Token] -- ^ Tokens to consider
-> Bool -- ^ Result
key _ [] = False -- Key was not found
key k1 (Token k2 _:xs) = (k1 == k2) || key k1 xs -- If keys match, return true, otherwise enter recursion
-- | Looks up a key and returns the value if found
value :: BS.ByteString -- ^ Key to look for
-> [Token] -- ^ Tokens to consider
-> Maybe BS.ByteString -- ^ The value if the key was found
value _ [] = Nothing -- Key not found!
value k1 (Token k2 v:xs) = if k1 == k2 -- This assumes keys are unique
then v -- This returns the value of the key, if any value is associated
else value k1 xs -- Otherwise we continue our quest (in recursion)
-- | Retrieves value, and applies it to an Attoparsec parser
valueAs :: Atto.Parser a
-> BS.ByteString
-> [Token]
-> Maybe a
valueAs p k xs =
let parseValue bs =
case Atto.parseOnly p bs of
Left _ -> Nothing
Right r -> Just r
in case value k xs of
Nothing -> Nothing
Just v -> parseValue v
-- | Retrieves first line that starts with a certain token
line :: BS.ByteString -- ^ Token key to look for
-> [Line] -- ^ Lines to consider
-> Maybe Line -- ^ The line that starts with this key, if found
line _ [] = Nothing
line k1 (x:xs) =
case x of
Line _ (Token k2 _:_) -> if k1 == k2
then Just x
else line k1 xs
_ -> line k1 xs
-- | Returns status code of a reply.
statusCode :: [Line]
-> Integer
statusCode = lineStatusCode . head