    -- |
    -- Copyright:   (c) 2022 Andrew Lelechenko
    -- Licence:     BSD3
    -- Maintainer:  Andrew Lelechenko <andrew.lelechenko@gmail.com>
    --
    -- Builder for strict 'Text' and 'ByteString', based on linear types. It consistently
    -- outperforms "Data.Text.Lazy.Builder"
    -- from @text@ as well as a strict builder from @text-builder@,
    -- and scales better.
    module Data.Text.Builder.Linear (
      Builder (..),
      runBuilder,
      runBuilderBS,
      fromText,
      fromChar,
      fromAddr,
      fromDec,
      fromUnboundedDec,
      fromHex,
      fromDouble,
    ) where

    import Data.Bits (FiniteBits)
    import Data.ByteString.Internal (ByteString (..))
    import Data.Text.Internal (Text (..))
    import GHC.Exts (Addr#, IsString (..))

    import Data.Text.Builder.Linear.Buffer

    -- | Thin wrapper over 'Buffer' with a handy 'Semigroup' instance.
    --
    -- >>> :set -XOverloadedStrings -XMagicHash
    -- >>> fromText "foo" <> fromChar '_' <> fromAddr "bar"#
    -- "foo_bar"
    --
    -- Remember: this is a strict builder, so on contrary to "Data.Text.Lazy.Builder"
    -- for optimal performance you should use strict left folds instead of lazy right ones.
    --
    -- Note that (similar to other builders) concatenation of 'Builder's allocates
    -- thunks. This is to a certain extent mitigated by aggressive inlining,
    -- but it is faster to use 'Buffer' directly.
    newtype Builder = Builder {Builder -> Buffer %1 -> Buffer
    unBuilder ∷ Buffer ⊸ Buffer}

    -- | Run 'Builder' computation on an empty 'Buffer', returning strict 'Text'.
    --
    -- >>> :set -XOverloadedStrings -XMagicHash
    -- >>> runBuilder (fromText "foo" <> fromChar '_' <> fromAddr "bar"#)
    -- "foo_bar"
    --
    -- This function has a polymorphic arrow and thus can be used both in
    -- usual and linear contexts.
    runBuilder ∷ ∀ m. Builder %m → Text
    runBuilder :: forall (m :: Multiplicity). Builder %m -> Text
    runBuilder (Builder Buffer %1 -> Buffer
    f) = (Buffer %1 -> Buffer) %1 -> Text
    runBuffer Buffer %1 -> Buffer
    f
    {-# INLINE runBuilder #-}

    -- | Same as 'runBuilder', but returning a UTF-8 encoded strict 'ByteString'.
    runBuilderBS ∷ ∀ m. Builder %m → ByteString
    runBuilderBS :: forall (m :: Multiplicity). Builder %m -> ByteString
    runBuilderBS (Builder Buffer %1 -> Buffer
    f) = (Buffer %1 -> Buffer) %1 -> ByteString
    runBufferBS Buffer %1 -> Buffer
    f
    {-# INLINE runBuilderBS #-}

    instance Show Builder where
      show :: Builder -> String
    show (Builder Buffer %1 -> Buffer
    f) = Text -> String
    forall a. Show a => a -> String
    show ((Buffer %1 -> Buffer) %1 -> Text
    runBuffer Buffer %1 -> Buffer
    f)

    instance Semigroup Builder where
      Builder Buffer %1 -> Buffer
    f <> :: Builder -> Builder -> Builder
    <> Builder Buffer %1 -> Buffer
    g = (Buffer %1 -> Buffer) -> Builder
    Builder ((Buffer %1 -> Buffer) -> Builder)
    -> (Buffer %1 -> Buffer) -> Builder
    forall a b. (a -> b) -> a -> b
    $ \Buffer
    b → Buffer %1 -> Buffer
    g (Buffer %1 -> Buffer
    f Buffer
    b)
      {-# INLINE (<>) #-}

    instance Monoid Builder where
      mempty :: Builder
    mempty = (Buffer %1 -> Buffer) -> Builder
    Builder (\Buffer
    b → Buffer
    b)
      {-# INLINE mempty #-}

    -- | Use 'fromString' to create 'Builder' from 'String'.
    instance IsString Builder where
      fromString :: String -> Builder
    fromString = Text -> Builder
    fromText (Text -> Builder) -> (String -> Text) -> String -> Builder
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . String -> Text
    forall a. IsString a => String -> a
    fromString
      {-# INLINE fromString #-}

    -- | Create 'Builder', containing a given 'Text'.
    --
    -- >>> :set -XOverloadedStrings
    -- >>> fromText "foo" <> fromText "bar"
    -- "foobar"
    --
    -- For literal strings it is faster to use 'fromAddr' instead of 'fromText'.
    fromText ∷ Text → Builder
    fromText :: Text -> Builder
    fromText Text
    x = (Buffer %1 -> Buffer) -> Builder
    Builder ((Buffer %1 -> Buffer) -> Builder)
    -> (Buffer %1 -> Buffer) -> Builder
    forall a b. (a -> b) -> a -> b
    $ \Buffer
    b → Buffer
    b Buffer %1 -> Text -> Buffer
    |> Text
    x
    {-# INLINE fromText #-}

    -- | Create 'Builder', containing a given 'Char'.
    --
    -- >>> fromChar 'x' <> fromChar 'y'
    -- "xy"
    --
    -- In contrast to 'Data.Text.Lazy.Builder.singleton', it's a responsibility
    -- of the caller to sanitize surrogate code points with 'Data.Text.Internal.safe'.
    fromChar ∷ Char → Builder
    fromChar :: Char -> Builder
    fromChar Char
    x = (Buffer %1 -> Buffer) -> Builder
    Builder ((Buffer %1 -> Buffer) -> Builder)
    -> (Buffer %1 -> Buffer) -> Builder
    forall a b. (a -> b) -> a -> b
    $ \Buffer
    b → Buffer
    b Buffer %1 -> Char -> Buffer
    |>. Char
    x
    {-# INLINE fromChar #-}

    -- | Create 'Builder', containing a null-terminated UTF-8 string, specified by 'Addr#'.
    --
    -- >>> :set -XMagicHash
    -- >>> fromAddr "foo"# <> fromAddr "bar"#
    -- "foobar"
    --
    -- The literal string must not contain zero bytes @\\NUL@ and must be a valid UTF-8,
    -- these conditions are not checked.
    fromAddr ∷ Addr# → Builder
    fromAddr :: Addr# -> Builder
    fromAddr Addr#
    x = (Buffer %1 -> Buffer) -> Builder
    Builder ((Buffer %1 -> Buffer) -> Builder)
    -> (Buffer %1 -> Buffer) -> Builder
    forall a b. (a -> b) -> a -> b
    $ \Buffer
    b → Buffer
    b Buffer %1 -> Addr# -> Buffer
    |># Addr#
    x
    {-# INLINE fromAddr #-}

    -- | Create 'Builder', containing decimal representation of a given /bounded/ integer.
    --
    -- >>> fromChar 'x' <> fromDec (123 :: Int)
    -- "x123"
    fromDec ∷ (Integral a, FiniteBits a) ⇒ a → Builder
    fromDec :: forall a. (Integral a, FiniteBits a) => a -> Builder
    fromDec a
    x = (Buffer %1 -> Buffer) -> Builder
    Builder ((Buffer %1 -> Buffer) -> Builder)
    -> (Buffer %1 -> Buffer) -> Builder
    forall a b. (a -> b) -> a -> b
    $ \Buffer
    b → Buffer
    b Buffer %1 -> a -> Buffer
    forall a. (Integral a, FiniteBits a) => Buffer %1 -> a -> Buffer
    |>$ a
    x
    {-# INLINE fromDec #-}

    -- | Create 'Builder', containing decimal representation of a given /unbounded/ integer.
    --
    -- >>> fromChar 'x' <> fromUnboundedDec (1e24 :: Integer)
    -- "x1000000000000000000000000"
    --
    -- @since 0.1.3
    fromUnboundedDec ∷ Integral a ⇒ a → Builder
    fromUnboundedDec :: forall a. Integral a => a -> Builder
    fromUnboundedDec a
    x = (Buffer %1 -> Buffer) -> Builder
    Builder ((Buffer %1 -> Buffer) -> Builder)
    -> (Buffer %1 -> Buffer) -> Builder
    forall a b. (a -> b) -> a -> b
    $ \Buffer
    b → Buffer
    b Buffer %1 -> a -> Buffer
    forall a. Integral a => Buffer %1 -> a -> Buffer
    |>$$ a
    x
    {-# INLINE fromUnboundedDec #-}

    -- | Create 'Builder', containing hexadecimal representation of a given integer.
    --
    -- >>> :set -XMagicHash
    -- >>> fromAddr "0x"# <> fromHex (0x123def :: Int)
    -- "0x123def"
    fromHex ∷ (Integral a, FiniteBits a) ⇒ a → Builder
    fromHex :: forall a. (Integral a, FiniteBits a) => a -> Builder
    fromHex a
    x = (Buffer %1 -> Buffer) -> Builder
    Builder ((Buffer %1 -> Buffer) -> Builder)
    -> (Buffer %1 -> Buffer) -> Builder
    forall a b. (a -> b) -> a -> b
    $ \Buffer
    b → Buffer
    b Buffer %1 -> a -> Buffer
    forall a. (Integral a, FiniteBits a) => Buffer %1 -> a -> Buffer
    |>& a
    x
    {-# INLINE fromHex #-}

    -- | Create 'Builder', containing decimal representation of a given 'Double'.
    --
    -- >>> :set -XMagicHash
    -- >>> fromAddr "pi="# <> fromDouble pi
    -- "pi=3.141592653589793"
    fromDouble ∷ Double → Builder
    fromDouble :: Double -> Builder
    fromDouble Double
    x = (Buffer %1 -> Buffer) -> Builder
    Builder ((Buffer %1 -> Buffer) -> Builder)
    -> (Buffer %1 -> Buffer) -> Builder
    forall a b. (a -> b) -> a -> b
    $ \Buffer
    b → Buffer
    b Buffer %1 -> Double -> Buffer
    |>% Double
    x
    {-# INLINE fromDouble #-}