packages feed

packed-data-0.1.0.0: src/Data/Packed/Needs.hs

{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE TypeFamilies #-}

module Data.Packed.Needs (
    -- * Type
    Needs (..),
    withEmptyNeeds,
    finish,

    -- * Builders
    NeedsBuilder (..),
    NeedsWriter,
    NeedsWriter',
    (>>),
    mkNeedsBuilder,
    withNeeds,

    -- * Mixing Needs together
    concatNeeds,
    applyNeeds,

    -- * Internal
    unsafeCastNeeds,
    (:++:),
) where

import ByteString.StrictBuilder
import Data.Kind
import Data.Packed.Packed
import Data.Packed.Utils
import Prelude hiding ((>>))

-- | A buffer where packed values can be written
-- The order to write these values is defined by the 'l' type list
--
-- If 'p' is an empty list, then a value of type 't' can be extracted from that buffer.
-- (See 'finish')
newtype Needs (p :: [Type]) (t :: [Type]) = Needs Builder

unsafeCastNeeds :: Needs a b -> Needs c d
unsafeCastNeeds (Needs b) = Needs b

-- | A wrapper around a function that builds a 'Needs'
--
-- 'ps': The type of the expected input of the source 'Needs'
--
-- 'ts': The type of the final packed data of the source 'Needs'
--
-- 'pd': The type of the expected input of the resuling 'Needs'
--
-- 'td': The type of the final packed data of the resulting 'Needs'
--
-- __Note:__ It is an indexed monad.
newtype NeedsBuilder ps ts pd td = NeedsBuilder
    { runBuilder :: Needs ps ts -> Needs pd td
    }

(>>) :: NeedsBuilder p1 t1 p2 t2 -> NeedsBuilder p2 t2 p3 t3 -> NeedsBuilder p1 t1 p3 t3
(NeedsBuilder !b1) >> (NeedsBuilder !b2) = mkNeedsBuilder (\n -> b2 (b1 n))

-- | Shortcut type for 'NeedsBuilder'\'s that simply write a value to a 'Needs' without changing the final packed type
type NeedsWriter a r t = NeedsBuilder (a ': r) t r t

-- | Shortcut type for 'NeedsBuilder'\'s that simply write multiple values to a 'Needs' without changing the final packed type
type NeedsWriter' a r t = NeedsBuilder (a :++: r) t r t

mkNeedsBuilder :: (Needs ps ts -> Needs pd td) -> NeedsBuilder ps ts pd td
mkNeedsBuilder = NeedsBuilder

withEmptyNeeds :: NeedsBuilder a b x y -> Needs x y
withEmptyNeeds (NeedsBuilder !b) = b (Needs mempty)

withNeeds :: Needs x y -> NeedsBuilder x y x1 y1 -> Needs x1 y1
withNeeds needs (NeedsBuilder !next) = next needs

concatNeeds :: Needs p t -> NeedsBuilder '[] t1 p (t1 :++: t)
concatNeeds (Needs !b) = NeedsBuilder (\(Needs s) -> Needs (s <> b))

applyNeeds :: Needs '[] t1 -> NeedsBuilder (t1 :++: r) t r t
applyNeeds (Needs b) = NeedsBuilder (\(Needs s) -> Needs (s <> b))

-- | Turns a 'Needs' value (that does not expect to be written to) to a 'Data.Packed.Packed'
finish :: Needs '[] t -> Packed t
finish (Needs !b) = Packed (builderBytes b)