-- |
-- Module      : Foundation.Collection.Buildable
-- License     : BSD-style
-- Maintainer  : foundation
-- Stability   : experimental
-- Portability : portable
--
-- An interface for collections that can be built incrementally.
--
module Foundation.Collection.Buildable
    ( Buildable(..)
    , Builder(..)
    , BuildingState(..)
    , builderLift
    , build_
    ) where

import           Basement.UArray
import           Basement.UArray.Mutable
import qualified Basement.BoxedArray as BA
import qualified Basement.String as S
import           Foundation.Collection.Element
import           Basement.Compat.Base
import           Basement.Monad
import           Basement.MutableBuilder
import           Basement.Compat.MonadTrans
import           Data.Kind (Type)

-- $setup
-- >>> import Control.Monad.ST
-- >>> import Basement.UArray
-- >>> import Basement.Compat.Base
-- >>> import Basement.OffsetSize

-- | Collections that can be built chunk by chunk.
--
-- Use the 'Monad' instance of 'Builder' to chain 'append' operations
-- and feed it into `build`:
--
-- >>> runST $ build 32 (append 'a' >> append 'b' >> append 'c') :: UArray Char
-- "abc"
class Buildable col where
    {-# MINIMAL append, build #-}

    -- | Mutable collection type used for incrementally writing chunks.
    type Mutable col :: Type -> Type

    -- | Unit of the smallest step possible in an `append` operation.
    --
    -- A UTF-8 character can have a size between 1 and 4 bytes, so this
    -- should be defined as 1 byte for collections of `Char`.
    type Step col

    append :: (PrimMonad prim) => Element col -> Builder col (Mutable col) (Step col) prim err ()

    build :: (PrimMonad prim)
          => Int -- ^ CountOf of a chunk
          -> Builder col (Mutable col) (Step col) prim err ()
          -> prim (Either err col)

builderLift :: (Buildable c, PrimMonad prim)
            => prim a
            -> Builder c (Mutable c) (Step c) prim err a
builderLift :: forall c (prim :: * -> *) a err.
(Buildable c, PrimMonad prim) =>
prim a -> Builder c (Mutable c) (Step c) prim err a
builderLift prim a
f = forall collection (mutCollection :: * -> *) step (state :: * -> *)
       err a.
State
  (Offset step,
   BuildingState collection mutCollection step (PrimState state),
   Maybe err)
  state
  a
-> Builder collection mutCollection step state err a
Builder forall a b. (a -> b) -> a -> b
$ forall s (m :: * -> *) a. (s -> m (a, s)) -> State s m a
State forall a b. (a -> b) -> a -> b
$ \(Offset (Step c)
i, BuildingState c (Mutable c) (Step c) (PrimState prim)
st, Maybe err
e) -> do
    a
ret <- prim a
f
    forall (m :: * -> *) a. Monad m => a -> m a
return (a
ret, (Offset (Step c)
i, BuildingState c (Mutable c) (Step c) (PrimState prim)
st, Maybe err
e))

build_ :: (Buildable c, PrimMonad prim)
       => Int -- ^ CountOf of a chunk
       -> Builder c (Mutable c) (Step c) prim () ()
       -> prim c
build_ :: forall c (prim :: * -> *).
(Buildable c, PrimMonad prim) =>
Int -> Builder c (Mutable c) (Step c) prim () () -> prim c
build_ Int
sizeChunksI Builder c (Mutable c) (Step c) prim () ()
ab = forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (\() -> forall a. [Char] -> a
internalError [Char]
"impossible output") forall {k} (cat :: k -> k -> *) (a :: k). Category cat => cat a a
id forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall col (prim :: * -> *) err.
(Buildable col, PrimMonad prim) =>
Int
-> Builder col (Mutable col) (Step col) prim err ()
-> prim (Either err col)
build Int
sizeChunksI Builder c (Mutable c) (Step c) prim () ()
ab

instance PrimType ty => Buildable (UArray ty) where
    type Mutable (UArray ty) = MUArray ty
    type Step (UArray ty) = ty
    append :: forall (prim :: * -> *) err.
PrimMonad prim =>
Element (UArray ty)
-> Builder
     (UArray ty) (Mutable (UArray ty)) (Step (UArray ty)) prim err ()
append = forall ty (state :: * -> *) err.
(PrimType ty, PrimMonad state) =>
ty -> Builder (UArray ty) (MUArray ty) ty state err ()
builderAppend
    {-# INLINE append #-}
    build :: forall (prim :: * -> *) err.
PrimMonad prim =>
Int
-> Builder
     (UArray ty) (Mutable (UArray ty)) (Step (UArray ty)) prim err ()
-> prim (Either err (UArray ty))
build = forall ty (m :: * -> *) err.
(PrimType ty, PrimMonad m) =>
Int
-> Builder (UArray ty) (MUArray ty) ty m err ()
-> m (Either err (UArray ty))
builderBuild
    {-# INLINE build #-}

instance Buildable (BA.Array ty) where
    type Mutable (BA.Array ty) = BA.MArray ty
    type Step (BA.Array ty) = ty

    append :: forall (prim :: * -> *) err.
PrimMonad prim =>
Element (Array ty)
-> Builder
     (Array ty) (Mutable (Array ty)) (Step (Array ty)) prim err ()
append = forall (state :: * -> *) ty err.
PrimMonad state =>
ty -> Builder (Array ty) (MArray ty) ty state err ()
BA.builderAppend
    {-# INLINE append #-}
    build :: forall (prim :: * -> *) err.
PrimMonad prim =>
Int
-> Builder
     (Array ty) (Mutable (Array ty)) (Step (Array ty)) prim err ()
-> prim (Either err (Array ty))
build = forall (m :: * -> *) ty err.
PrimMonad m =>
Int
-> Builder (Array ty) (MArray ty) ty m err ()
-> m (Either err (Array ty))
BA.builderBuild
    {-# INLINE build #-}

instance Buildable S.String where
    type Mutable S.String = S.MutableString
    type Step S.String = Word8

    append :: forall (prim :: * -> *) err.
PrimMonad prim =>
Element String
-> Builder String (Mutable String) (Step String) prim err ()
append = forall (state :: * -> *) err.
PrimMonad state =>
Char -> Builder String MutableString Word8 state err ()
S.builderAppend
    {-# INLINE append #-}
    build :: forall (prim :: * -> *) err.
PrimMonad prim =>
Int
-> Builder String (Mutable String) (Step String) prim err ()
-> prim (Either err String)
build = forall (m :: * -> *) err.
PrimMonad m =>
Int
-> Builder String MutableString Word8 m err ()
-> m (Either err String)
S.builderBuild
    {-# INLINE build #-}