packages feed

dahdit-0.2.0: src/Dahdit/BinaryRep.hs

{-# LANGUAGE UndecidableInstances #-}

module Dahdit.BinaryRep
  ( BinaryRep (..)
  , ViaBoundedEnum (..)
  , ViaIntegral (..)
  , ViaBinaryRep (..)
  )
where

import Dahdit.Binary (Binary (..))
import Dahdit.Sizes (ByteSized (..), StaticByteSized (..))
import Data.Proxy (Proxy (..))

class Binary x => BinaryRep x a | a -> x where
  fromBinaryRep :: x -> Either String a
  toBinaryRep :: a -> x

newtype ViaBoundedEnum x a = ViaBoundedEnum {unViaBoundedEnum :: a}

instance (Binary x, Integral x, Bounded a, Enum a) => BinaryRep x (ViaBoundedEnum x a) where
  fromBinaryRep x =
    let i = fromIntegral x
    in  if i < fromEnum (minBound :: a) || i > fromEnum (maxBound :: a)
          then Left ("Invalid enum value: " ++ show i)
          else Right (ViaBoundedEnum (toEnum i))
  toBinaryRep = fromIntegral . fromEnum . unViaBoundedEnum

newtype ViaIntegral x a = ViaIntegral {unViaIntegral :: a}

instance (Binary x, Integral x, Integral a) => BinaryRep x (ViaIntegral x a) where
  fromBinaryRep = Right . ViaIntegral . fromIntegral
  toBinaryRep = fromIntegral . unViaIntegral

newtype ViaBinaryRep a = ViaBinaryRep {unViaBinaryRep :: a}

instance (ByteSized x, BinaryRep x a) => ByteSized (ViaBinaryRep a) where
  byteSize = byteSize . toBinaryRep . unViaBinaryRep

instance (StaticByteSized x, BinaryRep x a) => StaticByteSized (ViaBinaryRep a) where
  staticByteSize _ = staticByteSize (Proxy :: Proxy x)

instance BinaryRep x a => Binary (ViaBinaryRep a) where
  get = get >>= either fail (pure . ViaBinaryRep) . fromBinaryRep
  put = put . toBinaryRep . unViaBinaryRep