AttoBencode / src / Data / AttoBencode / Types.hs

{-# LANGUAGE FlexibleInstances #-}

module Data.AttoBencode.Types
    ( BValue(..)
    , Dict
    , FromBencode(..)
    , ToBencode(..)
    ) where

import qualified Data.Map as M
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as B
import Data.Traversable (traverse)

-- | The Haskell data type for Bencode values
data BValue = BString !ByteString
            | BInt !Integer
            | BList ![BValue]
            | BDict !Dict
    deriving (Show, Eq)

-- | A Bencode dictionary is a map from ByteStrings to Bencode values
type Dict = M.Map ByteString BValue

-- | A type that can be converted to a BValue
class ToBencode a where
    toBencode :: a -> BValue

-- | A type that can be converted from a BValue. The conversion can fail.
class FromBencode a where
    fromBencode :: BValue -> Maybe a

instance ToBencode ByteString where
    toBencode = BString

instance ToBencode String where
    toBencode = BString . B.pack

instance ToBencode Integer where
    toBencode = BInt

instance ToBencode Int where
    toBencode = BInt . fromIntegral

instance (ToBencode a) => ToBencode [a] where
    toBencode = BList . map toBencode

instance (ToBencode a) => ToBencode (M.Map ByteString a) where
    toBencode = BDict . toBencode

instance (ToBencode a) => ToBencode [(ByteString, a)] where
    toBencode = BDict . M.fromList . map (\(k, v) -> (k, toBencode v))

instance ToBencode BValue where
    toBencode = id
-- TODO: make sure these are inlined

instance FromBencode ByteString where
    fromBencode (BString bs) = Just bs
    fromBencode _            = Nothing

instance FromBencode Integer where
    fromBencode (BInt n) = Just n
    fromBencode _        = Nothing

instance (FromBencode a) => FromBencode (M.Map ByteString a) where
    fromBencode (BDict d) = traverse fromBencode d
    fromBencode _         = Nothing

instance (FromBencode a) => FromBencode [a] where
    fromBencode (BList l) = sequence $ map fromBencode l
    fromBencode _         = Nothing
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.