AttoBencode / src / Data / AttoBencode / Types.hs

{-# LANGUAGE FlexibleInstances #-}

module Data.AttoBencode.Types 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) = traverse fromBencode l
    fromBencode _         = Nothing