module Data.Bencodex.Reader
( Result (..)
, bValue
, byteString
, decodeLazy
, decodeStrict
, false
, integer
, list
, map'
, null'
, text
, true
) where
import Control.Monad
import Data.Functor
import Data.Word
import Data.Attoparsec.ByteString hiding (Result)
import Data.Attoparsec.ByteString.Lazy as LP
import Data.ByteString
import qualified Data.ByteString.Lazy as LB
import Data.HashMap.Strict
import Data.Text
import Data.Text.Encoding
import Data.Text.Encoding.Error
import Data.Bencodex.Types
decodeLazy :: LB.ByteString -> Result BValue
decodeLazy = LP.parse bValue
decodeStrict :: ByteString -> Either String BValue
decodeStrict = parseOnly bValue
bValue :: Parser BValue
bValue = choice
[ (null' $> BNull) <?> "null"
, (BBool <$> true) <?> "true"
, (BBool <$> false) <?> "false"
, (BInteger <$> integer) <?> "integer"
, (BByteString <$> byteString) <?> "byte string"
, (BText <$> text) <?> "unicode text"
, (BList <$> list) <?> "list"
, (BMap <$> map') <?> "dictionary"
]
skipWord8 :: Word8 -> Parser ()
skipWord8 = void . word8
null' :: Parser ()
null' = skipWord8 0x6e
true :: Parser Bool
true = do
skipWord8 0x74
return True
false :: Parser Bool
false = do
skipWord8 0x66
return False
digits :: Num a => Parser a
digits = do
digits' <- takeWhile1 (\ w -> 0x30 <= w && w <= 0x39)
return $ Data.ByteString.foldl'
(\ i b -> i * 10 + fromIntegral (b - 0x30))
0
digits'
integer :: Parser Integer
integer = do
skipWord8 0x69
sign <- option 1 (word8 0x2d $> -1)
digits' <- digits
skipWord8 0x65
return $ sign * digits'
byteString :: Parser ByteString
byteString = do
size' <- digits
skipWord8 0x3a
Data.Attoparsec.ByteString.take size'
text :: Parser Text
text = do
skipWord8 0x75
size' <- digits
skipWord8 0x3a
bytes <- Data.Attoparsec.ByteString.take size'
case decodeUtf8' bytes of
Right decoded -> return decoded
Left (DecodeError msg _) -> fail msg
Left e -> fail ("unexpected error: " ++ show e)
list :: Parser [BValue]
list = do
skipWord8 0x6c
elements <- many' bValue
skipWord8 0x65
return elements
map' :: Parser (HashMap BKey BValue)
map' = do
skipWord8 0x64
pairs <- many' $ do
key <- bKey
value <- bValue
return (key, value)
skipWord8 0x65
return $ Data.HashMap.Strict.fromList pairs
bKey :: Parser BKey
bKey = choice
[ BByteStringKey <$> byteString
, BTextKey <$> text
]