Portability | portable |
---|---|
Stability | stable |
Maintainer | pxqr.sta@gmail.com |
Safe Haskell | Trustworthy |
This module provides convinient and fast way to serialize, deserealize and construct/destructure Bencoded values with optional fields.
It supports four different types of values:
- byte strings — represented as
ByteString
; - integers — represented as
Integer
; - lists - represented as ordinary lists;
- dictionaries — represented as
Map
;
To serialize any other types we need to make conversion. To
make conversion more convenient there is type class for it:
BEncodable
. Any textual strings are considered as UTF8 encoded
Text
.
The complete Augmented BNF syntax for bencoding format is:
<BE> ::= <DICT> | <LIST> | <INT> | <STR> <DICT> ::= "d" 1 * (<STR> <BE>) "e" <LIST> ::= "l" 1 * <BE> "e" <INT> ::= "i" <SNUM> "e" <STR> ::= <NUM> ":" n * <CHAR>; where n equals the <NUM> <SNUM> ::= "-" <NUM> / <NUM> <NUM> ::= 1 * <DIGIT> <CHAR> ::= % <DIGIT> ::= "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
This module is considered to be imported qualified.
- type BInteger = Integer
- type BString = ByteString
- type BList = [BValue]
- type BDict = Map BKey BValue
- type BKey = ByteString
- data BValue
- ppBEncode :: BValue -> Doc
- class BEncode a where
- toBEncode :: a -> BValue
- fromBEncode :: BValue -> Result a
- type Result = Either String
- encode :: BValue -> ByteString
- decode :: ByteString -> Result BValue
- encoded :: BEncode a => a -> ByteString
- decoded :: BEncode a => ByteString -> Result a
- data Assoc
- (-->) :: BEncode a => BKey -> a -> Assoc
- (-->?) :: BEncode a => BKey -> Maybe a -> Assoc
- fromAssocs :: [Assoc] -> BValue
- fromAscAssocs :: [Assoc] -> BValue
- decodingError :: String -> Result a
- reqKey :: BEncode a => BDict -> BKey -> Result a
- optKey :: BEncode a => BDict -> BKey -> Result (Maybe a)
- (>--) :: BEncode a => BDict -> BKey -> Result a
- (>--?) :: BEncode a => BDict -> BKey -> Result (Maybe a)
- isInteger :: BValue -> Bool
- isString :: BValue -> Bool
- isList :: BValue -> Bool
- isDict :: BValue -> Bool
- builder :: BValue -> Builder
- parser :: Parser BValue
Datatype
type BString = ByteStringSource
type BKey = ByteStringSource
BEncode
is straightforward ADT for b-encoded values. Please
note that since dictionaries are sorted, in most cases we can
compare BEncoded values without serialization and vice versa.
Lists is not required to be sorted through.
ppBEncode :: BValue -> DocSource
Convert to easily readable JSON-like document. Typically used for debugging purposes.
Conversion
This class is used to define new datatypes that could be easily serialized using bencode format.
By default BEncodable
have a generic implementation; suppose
the following datatype:
data List a = Cons { _head :: a , __tail :: (List a) } | Nil deriving Generic
If we don't need to obey any particular specification or
standard, the default instance could be derived automatically
from the Generic
instance:
instance BEncodable a => BEncodable (List a)
Example of derived toBEncode
result:
> toBEncode (Cons 123 $ Cons 1 Nil) BDict (fromList [("head",BInteger 123),("tail",BList [])])
Note that '_' prefixes are omitted.
toBEncode :: a -> BValueSource
See an example of implementation here Assoc
fromBEncode :: BValue -> Result aSource
See an example of implementation here reqKey
.
BEncode Bool | |
BEncode Int | |
BEncode Int8 | |
BEncode Int16 | |
BEncode Int32 | |
BEncode Int64 | |
BEncode Word | |
BEncode Word8 | |
BEncode Word16 | |
BEncode Word32 | |
BEncode Word64 | |
BEncode () | |
BEncode Text | |
BEncode Version | |
BEncode BValue | |
BEncode BString | |
BEncode BInteger | |
BEncode a => BEncode [a] | |
(Eq a, BEncode a) => BEncode (Set a) | |
(BEncode a, BEncode b) => BEncode (a, b) | |
BEncode a => BEncode (Map BKey a) | |
(BEncode a, BEncode b, BEncode c) => BEncode (a, b, c) | |
(BEncode a, BEncode b, BEncode c, BEncode d) => BEncode (a, b, c, d) | |
(BEncode a, BEncode b, BEncode c, BEncode d, BEncode e) => BEncode (a, b, c, d, e) |
Serialization
encode :: BValue -> ByteStringSource
Convert bencoded value to raw bytestring according to the specification.
decode :: ByteString -> Result BValueSource
Try to convert raw bytestring to bencoded value according to specification.
encoded :: BEncode a => a -> ByteStringSource
The same as encode
but takes any bencodable value.
decoded :: BEncode a => ByteString -> Result aSource
The same as decode
but returns any bencodable value.
Dictionaries
Building
Assoc used to easily build dictionaries with required and optional keys. Suppose we have we following datatype we want to serialize:
data FileInfo = FileInfo { fileLength :: Integer , fileMD5sum :: Maybe ByteString , filePath :: [ByteString] , fileTags :: Maybe [Text] } deriving (Show, Read, Eq)
We need to make instance BEncodable FileInfo, though we don't
want to check the both maybes manually. The more declarative and
convenient way to define the toBEncode
method is to use
dictionary builders:
instance BEncodable FileInfo where toBEncode FileInfo {..} = fromAssocs [ "length" --> fileLength , "md5sum" -->? fileMD5sum , "path" --> filePath , "tags" -->? fileTags ] ...
(-->?) :: BEncode a => BKey -> Maybe a -> AssocSource
Like (-->) but if the value is not present then the key do not appear in resulting bencoded dictionary.
fromAssocs :: [Assoc] -> BValueSource
Build BEncode dictionary using key -> value description.
fromAscAssocs :: [Assoc] -> BValueSource
A faster version of fromAssocs
. Should be used only when keys
in builder list are sorted by ascending.
Extraction
decodingError :: String -> Result aSource
Typically used to throw an decoding error in fromBEncode; when BEncode value to match expected value.
reqKey :: BEncode a => BDict -> BKey -> Result aSource
Dictionary extractor are similar to dictionary builders, but play
the opposite role: they are used to define fromBEncode
method in
declarative style. Using the same FileInfo datatype fromBEncode
looks like:
instance BEncodable FileInfo where ... fromBEncode (BDict d) = FileInfo <$> d >-- "length" <*> d >--? "md5sum" <*> d >-- "path" <*> d >--? "tags" fromBEncode _ = decodingError "FileInfo"
The reqKey is used to extract required key — if lookup is failed then whole destructuring fail.
optKey :: BEncode a => BDict -> BKey -> Result (Maybe a)Source
Used to extract optional key — if lookup is failed returns
Nothing
.