packages feed

argo-0.2021.11.11: source/library/Argo/Type/Encoder.hs

module Argo.Type.Encoder where

import qualified Argo.Literal as Literal
import qualified Argo.Type.Config as Config
import qualified Argo.Type.Indent as Indent
import qualified Argo.Vendor.Builder as Builder
import qualified Argo.Vendor.Transformers as Trans
import qualified Control.Monad as Monad
import qualified Data.Functor.Identity as Identity
import qualified Data.Semigroup as Semigroup

type Encoder
    = Trans.ReaderT
          Config.Config
          (Trans.WriterT Builder.Builder Identity.Identity)

unwrap :: Config.Config -> Encoder a -> (a, Builder.Builder)
unwrap c = Identity.runIdentity . Trans.runWriterT . flip Trans.runReaderT c

run :: Config.Config -> Encoder a -> Builder.Builder
run c = snd . unwrap c

list
    :: Encoder ()
    -> Encoder ()
    -> Encoder ()
    -> (a -> Encoder ())
    -> [a]
    -> Encoder ()
list left right separator encode xs = do
    left
    case xs of
        [] -> pure ()
        x : ys -> do
            Trans.local Config.increaseLevel $ do
                maybeIndent <- makeIndent <$> Trans.ask
                withNewLine maybeIndent
                encode x
                Monad.forM_ ys $ \y -> do
                    separator
                    withNewLine maybeIndent
                    encode y
            maybeIndent <- makeIndent <$> Trans.ask
            withNewLine maybeIndent
    right

makeIndent :: Config.Config -> Maybe Builder.Builder
makeIndent config = case Config.indent config of
    Indent.Spaces spaces -> if spaces <= 0
        then Nothing
        else
            Just
            . Semigroup.stimesMonoid (Config.level config * spaces)
            $ Builder.word8 Literal.space
    Indent.Tab ->
        Just . Semigroup.stimesMonoid (Config.level config) $ Builder.word8
            Literal.horizontalTabulation

withNewLine :: Maybe Builder.Builder -> Encoder ()
withNewLine = maybe (pure ()) $ Trans.lift . Trans.tell . mappend
    (Builder.word8 Literal.newLine)