-- | functions for different ways of rendering log entries to bytestring builders
module Unclog.Render
  ( -- * rendering to bytestring builders

    -- ** default builders
    simpleBuilder
  , colouredBuilder

    -- ** helpers
  , builderWithFn
  , renderLevel
  , renderLocation
  , colourLevel
  , bracketBuilder
  )
where

import Chronos (SubsecondPrecision (SubsecondPrecisionFixed), builderUtf8_YmdHMS, timeToDatetime, w3c)
import Colourista.Pure (blue, formatWith, green, red, yellow)
import Data.ByteString.Builder qualified as BS
import Data.List qualified as List
import Data.Text.Encoding qualified as T
import Unclog.Common

-- | a simple, non-coloured builder which takes all of the information into account
simpleBuilder :: LogEntry -> BS.Builder
simpleBuilder :: LogEntry -> Builder
simpleBuilder = (Builder -> Builder) -> LogEntry -> Builder
builderWithFn Builder -> Builder
forall a. a -> a
id

-- |  a simple, coloured builder which takes all information into account but also makes it colourful
colouredBuilder :: LogEntry -> BS.Builder
colouredBuilder :: LogEntry -> Builder
colouredBuilder LogEntry
entry = (Builder -> Builder) -> LogEntry -> Builder
builderWithFn (LogLevel -> Builder -> Builder
colourLevel LogEntry
entry.level) LogEntry
entry

builderWithFn :: (BS.Builder -> BS.Builder) -> LogEntry -> BS.Builder
builderWithFn :: (Builder -> Builder) -> LogEntry -> Builder
builderWithFn Builder -> Builder
fn LogEntry
entry =
  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat
    [ Builder -> Builder
fn (Builder -> Builder) -> Builder -> Builder
forall a b. (a -> b) -> a -> b
$ Builder -> Builder
bracketBuilder (Builder -> Builder) -> Builder -> Builder
forall a b. (a -> b) -> a -> b
$ LogLevel -> Builder
renderLevel LogEntry
entry.level
    , Builder -> Builder
bracketBuilder (Builder -> Builder) -> Builder -> Builder
forall a b. (a -> b) -> a -> b
$ SubsecondPrecision -> DatetimeFormat -> Datetime -> Builder
builderUtf8_YmdHMS (Int -> SubsecondPrecision
SubsecondPrecisionFixed Int
0) DatetimeFormat
w3c (Time -> Datetime
timeToDatetime LogEntry
entry.timestamp)
    , Builder -> Builder
bracketBuilder (Builder -> Builder) -> Builder -> Builder
forall a b. (a -> b) -> a -> b
$ Location -> Builder
renderLocation LogEntry
entry.location
    , Char -> Builder
BS.charUtf8 Char
' '
    , StrictByteString -> Builder
BS.byteString LogEntry
entry.payload
    , Char -> Builder
BS.charUtf8 Char
'\n'
    ]

-- | surround a builder with brackets
bracketBuilder :: BS.Builder -> BS.Builder
bracketBuilder :: Builder -> Builder
bracketBuilder Builder
a = Char -> Builder
BS.charUtf8 Char
'[' Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
a Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Char -> Builder
BS.charUtf8 Char
']'

-- | render the log level with even spacing
renderLevel :: LogLevel -> BS.Builder
renderLevel :: LogLevel -> Builder
renderLevel = \case
  LogLevel
Debug -> Builder
"Debug"
  LogLevel
Info -> Builder
"Info "
  LogLevel
Warn -> Builder
"Warn "
  LogLevel
Fatal -> Builder
"Fatal"

-- | colour a builder according to the loglevel
colourLevel :: LogLevel -> BS.Builder -> BS.Builder
colourLevel :: LogLevel -> Builder -> Builder
colourLevel =
  [Builder] -> Builder -> Builder
forall str. (IsString str, Semigroup str) => [str] -> str -> str
formatWith ([Builder] -> Builder -> Builder)
-> (LogLevel -> [Builder]) -> LogLevel -> Builder -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. \case
    LogLevel
Debug -> [Builder
forall str. IsString str => str
blue]
    LogLevel
Info -> [Builder
forall str. IsString str => str
green]
    LogLevel
Warn -> [Builder
forall str. IsString str => str
yellow]
    LogLevel
Fatal -> [Builder
forall str. IsString str => str
red]

-- | render a source location
renderLocation :: Location -> BS.Builder
renderLocation :: Location -> Builder
renderLocation MkLocation {Text
locpkg :: Text
locpkg :: Location -> Text
locpkg, Text
locmod :: Text
locmod :: Location -> Text
locmod, locpos :: Location -> Position
locpos = MkPosition {Int
posLeft :: Int
posLeft :: Position -> Int
posLeft, Int
posRight :: Int
posRight :: Position -> Int
posRight}} =
  [Builder] -> Builder
forall a. Monoid a => [a] -> a
mconcat ([Builder] -> Builder) -> [Builder] -> Builder
forall a b. (a -> b) -> a -> b
$
    Builder -> [Builder] -> [Builder]
forall a. a -> [a] -> [a]
List.intersperse
      (Char -> Builder
BS.charUtf8 Char
':')
      [ Text -> Builder
T.encodeUtf8Builder Text
locpkg
      , Text -> Builder
T.encodeUtf8Builder Text
locmod
      , Int -> Builder
BS.intDec Int
posLeft
      , Int -> Builder
BS.intDec Int
posRight
      ]