-- |
-- Module      : Basement.String.Builder
-- License     : BSD-style
-- Maintainer  : Foundation
--
-- String builder

{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Basement.String.Builder
    ( Builder
    , run
    , runUnsafe

    -- * Emit functions
    , emit
    , emitChar

    -- * unsafe
    , unsafeStringBuilder
    ) where


import qualified Basement.Block.Base as Block (length)
import qualified Basement.Block.Builder as Block
import           Basement.Compat.Base
import           Basement.Compat.Semigroup
import           Basement.Monad
import           Basement.String (String, ValidationFailure, Encoding (UTF8), fromBytes)
import           Basement.UArray.Base (UArray)
import qualified Basement.UArray.Base as A

newtype Builder = Builder Block.Builder
  deriving (NonEmpty Builder -> Builder
Builder -> Builder -> Builder
forall b. Integral b => b -> Builder -> Builder
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
stimes :: forall b. Integral b => b -> Builder -> Builder
$cstimes :: forall b. Integral b => b -> Builder -> Builder
sconcat :: NonEmpty Builder -> Builder
$csconcat :: NonEmpty Builder -> Builder
<> :: Builder -> Builder -> Builder
$c<> :: Builder -> Builder -> Builder
Semigroup, Semigroup Builder
Builder
[Builder] -> Builder
Builder -> Builder -> Builder
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
mconcat :: [Builder] -> Builder
$cmconcat :: [Builder] -> Builder
mappend :: Builder -> Builder -> Builder
$cmappend :: Builder -> Builder -> Builder
mempty :: Builder
$cmempty :: Builder
Monoid)

unsafeStringBuilder :: Block.Builder -> Builder
unsafeStringBuilder :: Builder -> Builder
unsafeStringBuilder = Builder -> Builder
Builder
{-# INLINE unsafeStringBuilder #-}

run :: PrimMonad prim => Builder -> prim (String, Maybe ValidationFailure, UArray Word8)
run :: forall (prim :: * -> *).
PrimMonad prim =>
Builder -> prim (String, Maybe ValidationFailure, UArray Word8)
run (Builder Builder
builder) = do
    Block Word8
block <- forall (prim :: * -> *).
PrimMonad prim =>
Builder -> prim (Block Word8)
Block.run Builder
builder
    let array :: UArray Word8
array = forall ty. Offset ty -> CountOf ty -> UArrayBackend ty -> UArray ty
A.UArray Offset Word8
0 (forall ty. PrimType ty => Block ty -> CountOf ty
Block.length Block Word8
block) (forall ty. Block ty -> UArrayBackend ty
A.UArrayBA Block Word8
block)
    forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ Encoding
-> UArray Word8 -> (String, Maybe ValidationFailure, UArray Word8)
fromBytes Encoding
UTF8 UArray Word8
array

-- | run the given builder and return the generated String
--
-- prefer `run`
runUnsafe :: PrimMonad prim => Builder -> prim String
runUnsafe :: forall (prim :: * -> *). PrimMonad prim => Builder -> prim String
runUnsafe (Builder Builder
builder) = forall (prim :: * -> *). PrimMonad prim => Builder -> prim String
Block.unsafeRunString Builder
builder

-- | add a string in the builder
emit :: String -> Builder
emit :: String -> Builder
emit = Builder -> Builder
Builder forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. String -> Builder
Block.emitString

-- | emit a UTF8 char in the builder
emitChar :: Char -> Builder
emitChar :: Char -> Builder
emitChar = Builder -> Builder
Builder forall {k} (cat :: k -> k -> *) (b :: k) (c :: k) (a :: k).
Category cat =>
cat b c -> cat a b -> cat a c
. Char -> Builder
Block.emitUTF8Char