packages feed

calligraphy-0.1.5: src/Calligraphy/Util/Printer.hs

{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Calligraphy.Util.Printer
  ( Printer,
    Prints,
    runPrinter,
    indent,
    brack,
    strLn,
    textLn,
    showLn,
  )
where

import Control.Monad.RWS
import Data.Text (Text)
import qualified Data.Text.Lazy as TL
import Data.Text.Lazy.Builder (Builder)
import qualified Data.Text.Lazy.Builder as TB

-- | An monadic interface to a fairly primitive line printer.
-- It maintains an indentation level, and provides efficient concatenation through 'Builder', and that's it.
newtype Printer a = Printer {_unPrinter :: RWS Int () Builder a}
  deriving newtype (Functor, Applicative, Monad)
  deriving (Semigroup, Monoid) via (Ap Printer a)

type Prints a = a -> Printer ()

runPrinter :: Printer () -> Text
runPrinter (Printer p) = TL.toStrict . TB.toLazyText . fst $ execRWS p 0 mempty

{-# INLINE indent #-}
indent :: Printer a -> Printer a
indent (Printer p) = Printer $ local (+ 4) p

{-# INLINE line #-}
line :: Builder -> Printer ()
line t = Printer $ do
  n <- ask
  modify $
    flip mappend $
      fold (replicate n (TB.singleton ' ')) <> t <> TB.singleton '\n'

{-# INLINE brack #-}
brack :: String -> String -> Printer a -> Printer a
brack pre post inner = strLn pre *> indent inner <* strLn post

{-# INLINE strLn #-}
strLn :: String -> Printer ()
strLn = line . TB.fromString

{-# INLINE textLn #-}
textLn :: Text -> Printer ()
textLn = line . TB.fromText

{-# INLINE showLn #-}
showLn :: (Show a) => a -> Printer ()
showLn = strLn . show