-- |
-- 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
(Builder -> Builder -> Builder)
-> (NonEmpty Builder -> Builder)
-> (forall b. Integral b => b -> Builder -> Builder)
-> Semigroup 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
$c<> :: Builder -> Builder -> Builder
<> :: Builder -> Builder -> Builder
$csconcat :: NonEmpty Builder -> Builder
sconcat :: NonEmpty Builder -> Builder
$cstimes :: forall b. Integral b => b -> Builder -> Builder
stimes :: forall b. Integral b => b -> Builder -> Builder
Semigroup, Semigroup Builder
Builder
Semigroup Builder =>
Builder
-> (Builder -> Builder -> Builder)
-> ([Builder] -> Builder)
-> Monoid Builder
[Builder] -> Builder
Builder -> Builder -> Builder
forall a.
Semigroup a =>
a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
$cmempty :: Builder
mempty :: Builder
$cmappend :: Builder -> Builder -> Builder
mappend :: Builder -> Builder -> Builder
$cmconcat :: [Builder] -> Builder
mconcat :: [Builder] -> 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 <- Builder -> prim (Block Word8)
forall (prim :: * -> *).
PrimMonad prim =>
Builder -> prim (Block Word8)
Block.run Builder
builder
    let array :: UArray Word8
array = Offset Word8
-> CountOf Word8 -> UArrayBackend Word8 -> UArray Word8
forall ty. Offset ty -> CountOf ty -> UArrayBackend ty -> UArray ty
A.UArray Offset Word8
0 (Block Word8 -> CountOf Word8
forall ty. PrimType ty => Block ty -> CountOf ty
Block.length Block Word8
block) (Block Word8 -> UArrayBackend Word8
forall ty. Block ty -> UArrayBackend ty
A.UArrayBA Block Word8
block)
    (String, Maybe ValidationFailure, UArray Word8)
-> prim (String, Maybe ValidationFailure, UArray Word8)
forall a. a -> prim a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ((String, Maybe ValidationFailure, UArray Word8)
 -> prim (String, Maybe ValidationFailure, UArray Word8))
-> (String, Maybe ValidationFailure, UArray Word8)
-> prim (String, Maybe ValidationFailure, UArray Word8)
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) = Builder -> prim String
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 (Builder -> Builder) -> (String -> Builder) -> String -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
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 (Builder -> Builder) -> (Char -> Builder) -> Char -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
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