-- | 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