packages feed

fused-effects-logger-0.0.1.1: src/Control/Carrier/Logger/IO.hs

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}

-- | An IO carrier for the 'Logger' effect that writes logs to a 'Handle'
-- selected by the provided function.
module Control.Carrier.Logger.IO (
  LoggerIOC (LoggerIOC),

  -- * Helpers
  HandleSelector,
  singleHandle,

  -- * Carrier implementation
  runLoggerIO,
  runStdoutLoggerIO,
  runStderrLoggerIO,
) where

import Control.Algebra (Algebra (..), (:+:) (..))
import Control.Carrier.Reader (ReaderC, runReader)
import Control.Effect.Logger (Logger (..))
import Control.Effect.Reader (ask)
import Control.Monad.IO.Class (MonadIO (..))
import Control.Monad.Logger (
  Loc,
  LogLevel,
  LogSource,
  LogStr,
  ToLogStr (..),
  defaultLogStr,
  fromLogStr,
 )
import qualified Data.ByteString.Char8 as B (hPutStr)
import System.IO (Handle, stderr, stdout)

-- | Algebra for a simple IO logger. The inner function allows for selecting a
-- 'Handle' according to something like 'LogLevel'.
newtype LoggerIOC f m a = LoggerIOC
  { runLoggerIOC :: ReaderC f m a
  }
  deriving (Applicative, Functor, Monad, MonadIO)

type HandleSelector = Loc -> LogSource -> LogLevel -> LogStr -> Handle

-- | Helper to run IO loggers by writing to the same 'Handle' every time.
singleHandle
  :: Handle
  -> HandleSelector
singleHandle h _ _ _ _ = h

instance
  forall sig m
   . ( Algebra sig m
     , MonadIO m
     )
  => Algebra (Logger :+: sig) (LoggerIOC HandleSelector m)
  where
  alg hdl sig ctx = case sig of
    L (LoggerLog loc src lvl msg) -> LoggerIOC $ do
      f <- ask
      ctx
        <$ liftIO
          ( B.hPutStr
              (f loc src lvl (toLogStr msg))
              (fromLogStr (defaultLogStr loc src lvl (toLogStr msg)))
          )
    R other -> LoggerIOC (alg (runLoggerIOC . hdl) (R other) ctx)

-- | Run logger, writing to a 'Handle' selected by a function @f@.
runLoggerIO
  :: HandleSelector
  -- ^ @f@
  -> LoggerIOC HandleSelector m a
  -> m a
runLoggerIO f = runReader f . runLoggerIOC

-- | Run logger, writing everything to 'stdout'.
runStdoutLoggerIO
  :: LoggerIOC HandleSelector m a
  -> m a
runStdoutLoggerIO = runLoggerIO (singleHandle stdout)

-- | Run logger, writing everything to 'stderr'.
runStderrLoggerIO
  :: LoggerIOC HandleSelector m a
  -> m a
runStderrLoggerIO = runLoggerIO (singleHandle stderr)