module Data.Serialize.Descriptor(
    Descriptor(Descriptor),
    unwrapGet,
    unwrapPut,
    serialize,
    deserialize
) where

import Data.ByteString (ByteString)
import Data.Serialize.Get
import Data.Serialize.Put

-- | 'Descriptor s a' is an applicative functor that describes the binary structure for a structure 's' while deserializing value 'a'.
newtype Descriptor s a = Descriptor {
    unwrapDescriptor :: (Get a, s -> Put)
}

-- | 'unwrapGet desc' takes a 'Descriptor' and returns only the internal 'Get' monad.
unwrapGet :: Descriptor s a -> Get a
unwrapGet = fst . unwrapDescriptor

-- | 'unwrapPut s desc' takes the structure being described and a 'Descriptor' for it, and returns the internal 'Put' monad.
unwrapPut :: s -> Descriptor s a -> Put
unwrapPut s = ($ s) . snd . unwrapDescriptor

-- | Convenience function for 'runPut . unwrapPut s'
serialize :: s -> Descriptor s a -> ByteString
serialize s = runPut . unwrapPut s

-- | Convenience function for 'flip runGet bs . unwrapGet'
deserialize :: ByteString -> Descriptor s s -> Either String s
deserialize bs = flip runGet bs . unwrapGet

instance Functor (Descriptor s) where
  fmap f (Descriptor (g, p)) = Descriptor (f <$> g, p)

instance Applicative (Descriptor s) where
  pure a = Descriptor (pure a, \_ -> pure ())
  (Descriptor (f, p)) <*> (Descriptor (g, p')) =
    Descriptor (f <*> g, \s' -> p s' >> p' s')