packages feed

grapesy-1.2.0: src/Network/GRPC/Util/Exception/Doc.hs

-- | Barebones rendering abstraction for nested indentation
module Network.GRPC.Util.Exception.Doc (
    Doc(..)
    -- * Construction
  , fromLines
  , withHeader
    -- * Rendering
  , renderDoc
  ) where

import Data.String
import Data.Semigroup

import Data.Foldable qualified as Foldable

{-------------------------------------------------------------------------------
  Definition
-------------------------------------------------------------------------------}

data Doc =
    FromString String
  | VCat [Doc]
  | Indent Int Doc

{-------------------------------------------------------------------------------
  Construction
-------------------------------------------------------------------------------}

instance IsString Doc where
  fromString = FromString

instance Monoid Doc where
  mempty  = VCat []
  mconcat = VCat

instance Semigroup Doc where
  a <> b  = VCat [a, b]
  sconcat = VCat . Foldable.toList

fromLines :: String -> Doc
fromLines = mconcat . map fromString . lines

withHeader :: String -> Doc -> Doc
withHeader header body = mconcat [
      fromString header
    , Indent 2 body
    ]

{-------------------------------------------------------------------------------
  Rendering
-------------------------------------------------------------------------------}

renderDoc :: Doc -> String
renderDoc = \d ->
      unlines
    $ map (\(i, str) -> replicate i ' ' ++ str)
    $ go [(0, d)]
  where
    go :: [(Int, Doc)] -> [(Int, String)]
    go []            = []
    go ((i, d) : ds) =
        case d of
          FromString str   -> (i, str) : go ds
          VCat    ds'   -> go $ map (i,) ds' ++ ds
          Indent     i' d' -> go $ (i + i', d') : ds