module FuncTorrent.Bencode
(BVal(..)
, bValToBList
- , bValToBstr
+ , bValToBytestr
, bValToInfoDict
, bValToInteger
, bstrToString
import Data.ByteString (ByteString, length, concat)
import Data.ByteString.Char8 (unpack, pack)
import Data.Functor ((<$>))
-import Data.Map.Strict (Map, fromList, keys, (!))
+import Data.Map.Strict (Map, fromList, toList)
import Text.ParserCombinators.Parsec
import qualified Text.Parsec.ByteString as ParsecBS
+import Test.QuickCheck
data BVal = Bint Integer
| Bstr ByteString
| Bdict (Map String BVal)
deriving (Ord, Eq, Show)
+instance Arbitrary ByteString where
+ arbitrary = pack <$> arbitrary
+
+instance Arbitrary BVal where
+ arbitrary = sized bval
+ where
+ bval :: Int -> Gen BVal
+ bval 0 = oneof [ Bint <$> arbitrary
+ , Bstr <$> arbitrary]
+ bval n = oneof [ Bint <$> arbitrary
+ , Bstr <$> arbitrary
+ , Blist <$> vectorOf n (bval (n `div` 4))
+ , do keys <- vectorOf n arbitrary
+ vals <- vectorOf n (bval (n `div` 4))
+ return $ Bdict $ fromList $ zip keys vals ]
+
-- getters
bValToInteger :: BVal -> Maybe Integer
bValToInteger (Bint x) = Just x
bValToInteger _ = Nothing
-bValToBstr :: BVal -> Maybe ByteString
-bValToBstr (Bstr bs) = Just bs
-bValToBstr _ = Nothing
+bValToBytestr :: BVal -> Maybe ByteString
+bValToBytestr (Bstr bs) = Just bs
+bValToBytestr _ = Nothing
bValToBList :: BVal -> Maybe [BVal]
bValToBList (Blist lst) = Just lst
bValToInfoDict _ = Nothing
bstrToString :: BVal -> Maybe String
-bstrToString bval = unpack <$> bValToBstr bval
+bstrToString bval = unpack <$> bValToBytestr bval
-- $setup
-- >>> import Data.Either
-- Right ""
--
bencStr :: ParsecBS.Parser ByteString
-bencStr = do _ <- spaces
- ds <- many1 digit <* char ':'
+bencStr = do ds <- many1 digit <* char ':'
s <- count (read ds) anyChar
return (pack s)
-- >>> isLeft $ parse bencInt "Bint" (pack "i002e")
-- True
bencInt :: ParsecBS.Parser Integer
-bencInt = do _ <- spaces
- ds <- between (char 'i') (char 'e') numbers
+bencInt = do ds <- between (char 'i') (char 'e') numbers
return (read ds)
where numbers = do d' <- char '-' <|> digit
ds' <- many digit
-- >>> parse bencList "Blist" (pack "l4:spam4:eggsli42eee")
-- Right [Bstr "spam",Bstr "eggs",Blist [Bint 42]]
bencList :: ParsecBS.Parser [BVal]
-bencList = do _ <- spaces
- between (char 'l') (char 'e') (many bencVal)
+bencList = between (char 'l') (char 'e') (many bencVal)
-- | parse dict
--
encode :: BVal -> ByteString
encode (Bstr bs) = pack $ show (length bs) ++ ":" ++ unpack bs
encode (Bint i) = pack $ "i" ++ show i ++ "e"
-encode (Blist xs) = pack $ "l" ++ unpack (concat $ map encode xs) ++ "e"
-encode (Bdict d) = concat [concat ["d", encode . Bstr . pack $ k , encode (d ! k) , "e"] | k <- keys d]
+encode (Blist xs) = concat ["l", concat $ map encode xs, "e"]
+encode (Bdict d) = concat ["d", concat kvlist, "e"]
+ where
+ kvlist :: [ByteString]
+ kvlist = [encPair kv | kv <- toList d]
+ encPair (k, v) = concat [encode . Bstr . pack $ k, encode v]