{-# LANGUAGE CPP #-}
{-# LANGUAGE RankNTypes #-}
-- |
--
-- Builder structures to help with turning 'Json' into a textual encoding.
--
module Waargonaut.Encode.Builder where

import           Data.String                       (IsString, fromString)

#if !MIN_VERSION_base(4,11,0)
import           Data.Monoid                       ((<>))
#endif


import           Data.Text                         (Text)
import qualified Data.Text.Lazy.Builder            as T
import qualified Data.Text.Lazy.Builder.Int        as T

import           Data.ByteString                   (ByteString)
import qualified Data.ByteString.Builder           as B
import qualified Data.ByteString.Builder.Prim      as BP

import           Waargonaut.Types.Json             (JType (..), Json (..))
import           Waargonaut.Types.Whitespace       (WS)

import           Waargonaut.Encode.Builder.JArray  (jArrayBuilder)
import           Waargonaut.Encode.Builder.JNumber (jNumberBuilder)
import           Waargonaut.Encode.Builder.JObject (jObjectBuilder)
import           Waargonaut.Encode.Builder.JString (jStringBuilder)
import           Waargonaut.Encode.Builder.Types   (Builder (..))

-- | A 'T.Text' builder
textBuilder :: Builder Text T.Builder
textBuilder :: Builder Text Builder
textBuilder = (Char -> Builder)
-> (Text -> Builder) -> (Int -> Builder) -> Builder Text Builder
forall t b. (Char -> b) -> (t -> b) -> (Int -> b) -> Builder t b
Builder
  Char -> Builder
T.singleton
  Text -> Builder
T.fromText
  Int -> Builder
forall a. Integral a => a -> Builder
T.decimal

-- | A 'B.ByteString' builder
bsBuilder :: Builder ByteString B.Builder
bsBuilder :: Builder ByteString Builder
bsBuilder = (Char -> Builder)
-> (ByteString -> Builder)
-> (Int -> Builder)
-> Builder ByteString Builder
forall t b. (Char -> b) -> (t -> b) -> (Int -> b) -> Builder t b
Builder
  (BoundedPrim Char -> Char -> Builder
forall a. BoundedPrim a -> a -> Builder
BP.primBounded BoundedPrim Char
BP.charUtf8)
  ByteString -> Builder
B.byteString
  Int -> Builder
B.intDec

-- | A general builder function for working with 'JType' values.
--
jTypesBuilder
  :: ( IsString t
     , Monoid b
     )
  => Builder t b
  -> (Builder t b -> WS -> b)
  -> JType WS Json
  -> b
jTypesBuilder :: Builder t b -> (Builder t b -> WS -> b) -> JType WS Json -> b
jTypesBuilder Builder t b
bldr Builder t b -> WS -> b
s JType WS Json
jt =
  let
    (b
jBuilt, WS
tws') = case JType WS Json
jt of
      JNull     WS
tws -> (Builder t b -> t -> b
forall t b. Builder t b -> t -> b
fromChunk Builder t b
bldr (String -> t
forall a. IsString a => String -> a
fromString String
"null"),                          WS
tws)
      JBool Bool
b   WS
tws -> (Builder t b -> t -> b
forall t b. Builder t b -> t -> b
fromChunk Builder t b
bldr (String -> t
forall a. IsString a => String -> a
fromString (String -> t) -> String -> t
forall a b. (a -> b) -> a -> b
$ if Bool
b then String
"true" else String
"false"), WS
tws)
      JNum JNumber
jn   WS
tws -> (Builder t b -> JNumber -> b
forall b t. Monoid b => Builder t b -> JNumber -> b
jNumberBuilder Builder t b
bldr JNumber
jn,                                      WS
tws)
      JStr JString
js   WS
tws -> (Builder t b -> JString -> b
forall b t. Monoid b => Builder t b -> JString -> b
jStringBuilder Builder t b
bldr JString
js,                                      WS
tws)
      JArr JArray WS Json
js   WS
tws -> (Builder t b
-> (Builder t b -> WS -> b)
-> ((Builder t b -> WS -> b) -> Builder t b -> Json -> b)
-> JArray WS Json
-> b
forall b t ws a.
Monoid b =>
Builder t b
-> (Builder t b -> ws -> b)
-> ((Builder t b -> ws -> b) -> Builder t b -> a -> b)
-> JArray ws a
-> b
jArrayBuilder Builder t b
bldr Builder t b -> WS -> b
s (Builder t b -> WS -> b) -> Builder t b -> Json -> b
forall t b.
(IsString t, Monoid b) =>
(Builder t b -> WS -> b) -> Builder t b -> Json -> b
waargonautBuilder JArray WS Json
js,                   WS
tws)
      JObj JObject WS Json
jobj WS
tws -> (Builder t b
-> (Builder t b -> WS -> b)
-> ((Builder t b -> WS -> b) -> Builder t b -> Json -> b)
-> JObject WS Json
-> b
forall b t ws a.
Monoid b =>
Builder t b
-> (Builder t b -> ws -> b)
-> ((Builder t b -> ws -> b) -> Builder t b -> a -> b)
-> JObject ws a
-> b
jObjectBuilder Builder t b
bldr Builder t b -> WS -> b
s (Builder t b -> WS -> b) -> Builder t b -> Json -> b
forall t b.
(IsString t, Monoid b) =>
(Builder t b -> WS -> b) -> Builder t b -> Json -> b
waargonautBuilder JObject WS Json
jobj,                WS
tws)
  in
    b
jBuilt b -> b -> b
forall a. Semigroup a => a -> a -> a
<> Builder t b -> WS -> b
s Builder t b
bldr WS
tws'

-- | Using the given whitespace builder, create a builder for a given 'Json' value.
waargonautBuilder
  :: ( IsString t
     , Monoid b
     )
  => (Builder t b -> WS -> b)
  -> Builder t b
  -> Json
  -> b
waargonautBuilder :: (Builder t b -> WS -> b) -> Builder t b -> Json -> b
waargonautBuilder Builder t b -> WS -> b
ws Builder t b
bldr (Json JType WS Json
jt) =
  Builder t b -> (Builder t b -> WS -> b) -> JType WS Json -> b
forall t b.
(IsString t, Monoid b) =>
Builder t b -> (Builder t b -> WS -> b) -> JType WS Json -> b
jTypesBuilder Builder t b
bldr Builder t b -> WS -> b
ws JType WS Json
jt