-- |
-- 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 :: prim a -> Builder c (Mutable c) (Step c) prim err a
builderLift prim a
f = State
  (Offset (Step c),
   BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
  prim
  a
-> Builder c (Mutable c) (Step c) prim err a
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 (State
   (Offset (Step c),
    BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
   prim
   a
 -> Builder c (Mutable c) (Step c) prim err a)
-> State
     (Offset (Step c),
      BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
     prim
     a
-> Builder c (Mutable c) (Step c) prim err a
forall a b. (a -> b) -> a -> b
$ ((Offset (Step c),
  BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
 -> prim
      (a,
       (Offset (Step c),
        BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)))
-> State
     (Offset (Step c),
      BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
     prim
     a
forall s (m :: * -> *) a. (s -> m (a, s)) -> State s m a
State (((Offset (Step c),
   BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
  -> prim
       (a,
        (Offset (Step c),
         BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)))
 -> State
      (Offset (Step c),
       BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
      prim
      a)
-> ((Offset (Step c),
     BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
    -> prim
         (a,
          (Offset (Step c),
           BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)))
-> State
     (Offset (Step c),
      BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err)
     prim
     a
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
    (a,
 (Offset (Step c),
  BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err))
-> prim
     (a,
      (Offset (Step c),
       BuildingState c (Mutable c) (Step c) (PrimState prim), Maybe err))
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_ :: Int -> Builder c (Mutable c) (Step c) prim () () -> prim c
build_ Int
sizeChunksI Builder c (Mutable c) (Step c) prim () ()
ab = (() -> c) -> (c -> c) -> Either () c -> c
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (\() -> [Char] -> c
forall a. [Char] -> a
internalError [Char]
"impossible output") c -> c
forall k (cat :: k -> k -> *) (a :: k). Category cat => cat a a
id (Either () c -> c) -> prim (Either () c) -> prim c
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Int
-> Builder c (Mutable c) (Step c) prim () () -> prim (Either () c)
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 :: Element (UArray ty)
-> Builder
     (UArray ty) (Mutable (UArray ty)) (Step (UArray ty)) prim err ()
append = Element (UArray ty)
-> Builder
     (UArray ty) (Mutable (UArray ty)) (Step (UArray ty)) prim err ()
forall ty (state :: * -> *) err.
(PrimType ty, PrimMonad state) =>
ty -> Builder (UArray ty) (MUArray ty) ty state err ()
builderAppend
    {-# INLINE append #-}
    build :: Int
-> Builder
     (UArray ty) (Mutable (UArray ty)) (Step (UArray ty)) prim err ()
-> prim (Either err (UArray ty))
build = Int
-> Builder
     (UArray ty) (Mutable (UArray ty)) (Step (UArray ty)) prim err ()
-> prim (Either err (UArray ty))
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 :: Element (Array ty)
-> Builder
     (Array ty) (Mutable (Array ty)) (Step (Array ty)) prim err ()
append = Element (Array ty)
-> Builder
     (Array ty) (Mutable (Array ty)) (Step (Array ty)) prim err ()
forall (state :: * -> *) ty err.
PrimMonad state =>
ty -> Builder (Array ty) (MArray ty) ty state err ()
BA.builderAppend
    {-# INLINE append #-}
    build :: Int
-> Builder
     (Array ty) (Mutable (Array ty)) (Step (Array ty)) prim err ()
-> prim (Either err (Array ty))
build = Int
-> Builder
     (Array ty) (Mutable (Array ty)) (Step (Array ty)) prim err ()
-> prim (Either err (Array ty))
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 :: Element String
-> Builder String (Mutable String) (Step String) prim err ()
append = Element String
-> Builder String (Mutable String) (Step String) prim err ()
forall (state :: * -> *) err.
PrimMonad state =>
Char -> Builder String MutableString Word8 state err ()
S.builderAppend
    {-# INLINE append #-}
    build :: Int
-> Builder String (Mutable String) (Step String) prim err ()
-> prim (Either err String)
build = Int
-> Builder String (Mutable String) (Step String) prim err ()
-> prim (Either err String)
forall (m :: * -> *) err.
PrimMonad m =>
Int
-> Builder String MutableString Word8 m err ()
-> m (Either err String)
S.builderBuild
    {-# INLINE build #-}