bencode 0.6.0.0 → 0.6.1.0
raw patch · 6 files changed
+268/−43 lines, 6 filesdep +QuickCheckdep +bencodedep +hspecdep ~basenew-uploaderPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependencies added: QuickCheck, bencode, hspec, transformers, transformers-compat
Dependency ranges changed: base
API changes (from Hackage documentation)
- Data.BEncode: instance Binary BEncode
- Data.BEncode: instance Eq BEncode
- Data.BEncode: instance Ord BEncode
- Data.BEncode: instance Show BEncode
- Data.BEncode.Lexer: instance Eq Token
- Data.BEncode.Lexer: instance Show Token
- Data.BEncode.Parser: instance Alternative BParser
- Data.BEncode.Parser: instance Applicative BParser
- Data.BEncode.Parser: instance Functor BParser
- Data.BEncode.Parser: instance Monad BParser
- Data.BEncode.Parser: instance MonadPlus BParser
+ Data.BEncode: instance Data.Binary.Class.Binary Data.BEncode.BEncode
+ Data.BEncode: instance GHC.Classes.Eq Data.BEncode.BEncode
+ Data.BEncode: instance GHC.Classes.Ord Data.BEncode.BEncode
+ Data.BEncode: instance GHC.Show.Show Data.BEncode.BEncode
+ Data.BEncode.Lexer: instance GHC.Classes.Eq Data.BEncode.Lexer.Token
+ Data.BEncode.Lexer: instance GHC.Show.Show Data.BEncode.Lexer.Token
+ Data.BEncode.Parser: infixl 3 <|>
+ Data.BEncode.Parser: instance GHC.Base.Alternative Data.BEncode.Parser.BParser
+ Data.BEncode.Parser: instance GHC.Base.Applicative Data.BEncode.Parser.BParser
+ Data.BEncode.Parser: instance GHC.Base.Functor Data.BEncode.Parser.BParser
+ Data.BEncode.Parser: instance GHC.Base.Monad Data.BEncode.Parser.BParser
+ Data.BEncode.Parser: instance GHC.Base.MonadPlus Data.BEncode.Parser.BParser
+ Data.BEncode.Reader: bbytestring :: BReader ByteString
+ Data.BEncode.Reader: bint :: BReader Integer
+ Data.BEncode.Reader: bstring :: BReader String
+ Data.BEncode.Reader: data BReader a
+ Data.BEncode.Reader: dict :: String -> BReader a -> BReader a
+ Data.BEncode.Reader: instance GHC.Base.Alternative Data.BEncode.Reader.BReader
+ Data.BEncode.Reader: instance GHC.Base.Applicative Data.BEncode.Reader.BReader
+ Data.BEncode.Reader: instance GHC.Base.Functor Data.BEncode.Reader.BReader
+ Data.BEncode.Reader: instance GHC.Base.Monad Data.BEncode.Reader.BReader
+ Data.BEncode.Reader: instance GHC.Base.MonadPlus Data.BEncode.Reader.BReader
+ Data.BEncode.Reader: list :: BReader a -> BReader [a]
+ Data.BEncode.Reader: optional :: Alternative f => f a -> f (Maybe a)
+ Data.BEncode.Reader: runBReader :: BReader a -> BEncode -> Either String a
- Data.BEncode: BDict :: (Map String BEncode) -> BEncode
+ Data.BEncode: BDict :: Map String BEncode -> BEncode
- Data.BEncode.Parser: (<|>) :: Alternative f => forall a. f a -> f a -> f a
+ Data.BEncode.Parser: (<|>) :: Alternative f => f a -> f a -> f a
Files
- bencode.cabal +39/−32
- src/Data/BEncode.hs +4/−3
- src/Data/BEncode/Lexer.hs +1/−1
- src/Data/BEncode/Parser.hs +15/−7
- src/Data/BEncode/Reader.hs +130/−0
- tests/Spec.hs +79/−0
bencode.cabal view
@@ -1,40 +1,47 @@-Name: bencode-Version: 0.6.0.0-Maintainer: Christopher Reichert <creichert07@gmail.com>-Author: Lemmih (lemmih@gmail.com), Jesper Louis Andersen-Copyright: (c) 2005-2009, David Himmelstrup,- 2006 Lemmih <lemmih@gmail.com>,- 2005 Jesper Louis Andersen <jlouis@mongers.org>-License-File: LICENSE-License: BSD3-Build-Type: Simple-Category: Text-Tested-With: GHC == 7.10.1, GHC == 7.8.4, GHC == 7.8.3, GHC == 7.6.3-Cabal-Version: >= 1.10-Synopsis: Parser and printer for bencoded data.-Description:- Parser and printer for bencoded data.- .- Bencode (pronounced like B encode) is the encoding used by the- peer-to-peer file sharing system BitTorrent for storing and- transmitting loosely structured data.-+name: bencode+version: 0.6.1.0+synopsis: Parsers and printers for bencoded data.+description: Parsers and printers for bencoded data. Bencode (pronounced like B encode)+ is the encoding used by the peer-to-peer file sharing system BitTorrent for+ storing and transmitting loosely structured data.+license: BSD3+license-file: LICENSE+copyright: (c) 2005-2009, David Himmelstrup,+ (c) 2006 Lemmih <lemmih@gmail.com>,+ (c) 2005 Jesper Louis Andersen <jlouis@mongers.org>+author: Lemmih <lemmih@gmail.com>,+ Jesper Louis Andersen <jlouis@mongers.org>,+ Christopher Reichert <creichert07@gmail.com>+maintainer: Peter Simons <simons@cryp.to>+tested-with: GHC == 7.6.3, GHC == 7.8.4, GHC == 7.10.3, GHC == 8.0.2, GHC == 8.2.2,+ GHC == 8.4.4, GHC == 8.6.5, GHC == 8.8.2+category: Text+build-type: Simple+cabal-version: >= 1.10 -Source-Repository head+source-repository head type: git location: https://github.com/creichert/bencode --Library- GHC-Options: -Wall- Default-Extensions: PatternGuards- Hs-Source-Dirs: src- Default-Language: Haskell2010- Exposed-Modules: Data.BEncode+library+ exposed-modules: Data.BEncode Data.BEncode.Lexer Data.BEncode.Parser- Build-Depends: base<5- , parsec+ Data.BEncode.Reader+ hs-source-dirs: src+ build-depends: base == 4.*+ , binary , bytestring , containers- , binary+ , parsec+ , transformers+ , transformers-compat >= 0.4+ default-language: Haskell2010+ default-extensions: PatternGuards++test-suite spec+ type: exitcode-stdio-1.0+ main-is: Spec.hs+ hs-source-dirs: tests+ build-depends: base, QuickCheck, bencode, bytestring, containers, hspec+ default-language: Haskell2010
src/Data/BEncode.hs view
@@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- |--- Module : BEncode+-- Module : Data.BEncode -- Copyright : (c) 2005 Jesper Louis Andersen <jlouis@mongers.org> -- 2006 Lemmih <lemmih@gmail.com> -- License : BSD3@@ -53,7 +53,7 @@ Just e -> return e Nothing -> fail "Failed to parse BEncoded data" --- Source possition is pretty useless in BEncoded data. FIXME+-- Source position is pretty useless in BEncoded data. FIXME updatePos :: (SourcePos -> Token -> [Token] -> SourcePos) updatePos pos _ _ = pos @@ -92,7 +92,8 @@ bList = withToken TList $ fmap BList (many bParse) bDict :: BParser BEncode-bDict = withToken TDict $ fmap (BDict . Map.fromAscList) (checkList =<< many1 bAssocList)+bDict = withToken TDict $+ fmap (BDict . Map.fromAscList) (checkList =<< many bAssocList) where checkList lst = if lst /= sort lst then fail "dictionary not sorted" else return lst
src/Data/BEncode/Lexer.hs view
@@ -1,6 +1,6 @@ ----------------------------------------------------------------------------- -- |--- Module : BEncode.Lexer+-- Module : Data.BEncode.Lexer -- Copyright : (c) 2005 Jesper Louis Andersen <jlouis@mongers.org> -- 2006 Lemmih <lemmih@gmail.com> -- License : BSD3
src/Data/BEncode/Parser.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} ----------------------------------------------------------------------------- -- | -- Module : BParser@@ -9,7 +10,8 @@ -- -- A parsec style parser for BEncoded data ------------------------------------------------------------------------------module Data.BEncode.Parser+module Data.BEncode.Parser {-#+ DEPRECATED "Use \"Data.BEncode.Reader\" instead" #-} ( BParser , runParser , token@@ -30,12 +32,16 @@ import qualified Data.ByteString.Lazy.Char8 as L import qualified Data.Map as Map +#if MIN_VERSION_base(4,13,0)+import qualified Control.Monad.Fail as Fail+#endif+ data BParser a = BParser (BEncode -> Reply a) instance Alternative BParser where+ (<|>) = mplus empty = mzero- (<|>) a b = a `mplus` b instance MonadPlus BParser where mzero = BParser $ \_ -> Error "mzero"@@ -51,21 +57,23 @@ = Ok a BEncode | Error String - instance Applicative BParser where- pure = return- (<*>) = ap+ pure = return+ (<*>) = ap instance Monad BParser where (BParser p) >>= f = BParser $ \b -> case p b of Ok a b' -> runB (f a) b' Error str -> Error str return val = BParser $ Ok val+#if MIN_VERSION_base(4,13,0)+instance Fail.MonadFail BParser where+#endif fail str = BParser $ \_ -> Error str instance Functor BParser where- fmap fn p = do a <- p- return (fn a)+ fmap = liftM+ runParser :: BParser a -> BEncode -> Either String a runParser parser b = case runB parser b of
+ src/Data/BEncode/Reader.hs view
@@ -0,0 +1,130 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# OPTIONS_HADDOCK show-extensions #-}+-----------------------------------------------------------------------------+-- |+-- Module : Data.BEncode.Reader+-- Copyright : (c) 2015 Matthew Leon <ml@matthewleon.com>+-- License : BSD3+-- Maintainer : creichert07@gmail.com+-- Stability : experimental+-- Portability : portable+--+-- Reader monad and combinators for BEncoded data.+--+-- This is intended to replace the older "Data.BEncode.Parser" module.+--+-- Usage example:+--+-- >>> :set -XOverloadedStrings+-- >>> let bd = (BDict $ Map.fromList [("baz", BInt 1), ("foo", BString "bar")])+-- >>> :{+-- let bReader = do+-- baz <- dict "baz" bint+-- foo <- dict "foo" bstring+-- shouldBeNothing <- optional $ dict "optionalKey" bint+-- return (foo, baz, shouldBeNothing)+-- in runBReader bReader bd+-- :}+-- Right ("bar",1,Nothing)+-----------------------------------------------------------------------------++module Data.BEncode.Reader (+ -- * Reader Monad+ BReader, runBReader,+ -- * Combinators+ bint, bbytestring, bstring, optional, list, dict+ ) where++import Control.Applicative+import Control.Monad (MonadPlus)+import Control.Monad.Trans.Reader+import Control.Monad.Trans.Except+import qualified Data.ByteString.Lazy.Char8 as L+import qualified Data.Map as Map++import Data.BEncode++-----------------------------------------------------------------------------+-----------------------------------------------------------------------------++newtype BReader a = BReader (ExceptT String (Reader BEncode) a)+ deriving (Functor, Applicative, Alternative, Monad, MonadPlus)+-- ^Reader monad for extracting data from a BEncoded structure.++breader :: (BEncode -> (Either String a)) -> BReader a+breader = BReader . ExceptT . reader+-- ^BReader constructor. Private.++runBReader :: BReader a -> BEncode -> Either String a+runBReader (BReader br) = runReader $ runExceptT br+-- ^Run a BReader. See usage examples elsewhere in this file.++-----------------------------------------------------------------------------+-----------------------------------------------------------------------------++bbytestring :: BReader L.ByteString+bbytestring = breader $ \b -> case b of+ BString str -> return str+ _ -> Left $ "Expected BString, found: " ++ show b+-- ^ Usage same as bstring, below.+-- (sadly, doctests for this cause errors on GHC 7.4)++bstring :: BReader String+bstring = fmap L.unpack bbytestring+-- ^+-- >>> runBReader bstring (BString "foo")+-- Right "foo"+--++bint :: BReader Integer+bint = breader $ \b -> case b of+ BInt int -> return int+ _ -> Left $ "Expected BInt, found: " ++ show b+-- ^+-- >>> runBReader bint (BInt 42)+-- Right 42+--++list :: BReader a -> BReader [a]+list br = breader $ \b -> case b of+ BList bs -> mapM (runBReader br) bs+ _ -> Left $ "Not a list: " ++ show b+-- ^ Read a list of BEncoded data+--+-- >>> runBReader (list bint) (BList [BInt 1, BInt 2])+-- Right [1,2]+--+-- >>> runBReader (list bint) (BList [])+-- Right []+--+-- >>> let bs = (BList [BList [BString "foo", BString "bar"], BList []])+-- >>> runBReader (list $ list bstring) bs+-- Right [["foo","bar"],[]]++dict :: String -> BReader a -> BReader a+dict name br = breader $ \b -> case b of+ BDict bmap | (Just code) <- Map.lookup name bmap -> runBReader br code+ BDict _ -> Left $ "Name not found in dictionary: " ++ name+ _ -> Left $ "Not a dictionary: " ++ show b+-- ^ Read the values of a BDict corresponding to a string key+--+-- >>> let bd = (BDict $ Map.fromList [("bar", BInt 2), ("foo", BInt 1)])+-- >>> runBReader (dict "foo" bint) bd+-- Right 1+--+--+-- >>> :{+-- let bs = (BList [BDict $ Map.fromList [("baz", BInt 2),+-- ("foo", BString "bar")],+-- BDict $ Map.singleton "foo" (BString "bam")])+-- in runBReader (list $ dict "foo" bstring) bs+-- :}+-- Right ["bar","bam"]+--+-- >>> :{+-- let bd = (BDict $ Map.singleton "foo" (BList [+-- BString "foo", BString "bar"+-- ]))+-- in runBReader (dict "foo" $ list $ bstring) bd+-- :}+-- Right ["foo","bar"]
+ tests/Spec.hs view
@@ -0,0 +1,79 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-} -- orphan Arbitrary instance is fine++import qualified Data.Map as Map++import Test.Hspec+import Test.QuickCheck++import Data.BEncode+import Data.ByteString.Lazy (pack)++instance Arbitrary BEncode where arbitrary = sized bencode'++bencode' :: Int -> Gen BEncode+bencode' 0 = oneof [BInt `fmap` arbitrary, (BString . pack) `fmap` arbitrary]+bencode' n = oneof [+ BInt `fmap` arbitrary,+ (BString . pack) `fmap` arbitrary :: Gen BEncode,+ BList `fmap` (resize (n `div` 2) arbitrary),+ (BDict . Map.fromList) `fmap` (resize (n `div` 2) arbitrary)+ ]++main :: IO ()+main = hspec $ do++ let bll = BList [BInt (-1), BInt 0, BInt 1, BInt 2, BInt 3, BString "four"]++ describe "Data.BEncode encoding" $ do+ it "encodes integers" $+ bRead "i42e" `shouldBe` Just (BInt 42)+ it "encodes strings" $+ bRead "3:foo" `shouldBe` Just (BString "foo")+ it "encodes strings with special characters in Haskell source" $+ bRead "5:café" `shouldBe` Just (BString "café")+ it "encodes lists" $+ bRead "l5:helloi42eli-1ei0ei1ei2ei3e4:fouree"+ `shouldBe` Just (BList [ BString "hello", BInt 42, bll ])+ it "encodes nested lists" $+ bRead "ll5:helloi62eel3:fooee"+ `shouldBe` Just (BList+ [ BList [ BString "hello", BInt 62 ],+ BList [ BString "foo" ] ])+ it "encodes dictionaries" $+ bRead "d3:baz3:moo3:foo3:bare"+ `shouldBe` Just+ (BDict (Map.fromList+ [("baz",BString "moo"),("foo",BString "bar")]))+ it "encodes empty dictionaries" $+ bRead "de" `shouldBe` Just (BDict Map.empty)++ describe "Data.BEncode decoding" $ do+ -- TODO failing+ -- it "is the inverse of encoding" $ property $ \bencode ->+ -- (bRead . bPack) bencode == Just bencode+ it "decodes int" $+ bPack (BInt 42) `shouldBe` "i42e"+ it "decodes null int" $+ bPack (BInt 0) `shouldBe` "i0e"+ it "decodes negative int" $+ bPack (BInt (-42)) `shouldBe` "i-42e"+ it "decodes string" $ do+ bPack (BString "foo") `shouldBe` "3:foo"+ bPack (BString "") `shouldBe` "0:"+ it "decodes lists" $+ bPack (BList [BInt 1, BInt 2, BInt 3]) `shouldBe` "li1ei2ei3ee"+ it "decodes lists of lists" $+ bPack (BList [ BList [BInt 1], BInt 1, BInt 2, BInt 3])+ `shouldBe` "lli1eei1ei2ei3ee"+ it "decodes hash" $ do+ let d = Map.fromList [("foo", BString "bar"), ("baz",BString "qux")]+ bPack (BDict d) `shouldBe` "d3:baz3:qux3:foo3:bare"+ -- FIX+ -- it "decodes unicode" $ do+ -- bPack (BString "café") `shouldBe` "5:café"+ -- bPack (BList [BString "你好", BString "中文"]) `shouldBe` "l6:你好6:中文e"+ it "decodes lists of lists" $+ bRead "l5:helloi42eli-1ei0ei1ei2ei3e4:fouree"+ `shouldBe` Just (BList [ BString "hello", BInt 42, bll])