\subsection{Cardano.BM.Data.Backend}
\label{code:Cardano.BM.Data.Backend}

%if style == newcode
\begin{code}
{-# LANGUAGE DefaultSignatures     #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE RankNTypes            #-}
{-# LANGUAGE TypeFamilies          #-}

module Cardano.BM.Data.Backend
  ( Backend (..)
  , BackendKind (..)
  , BackendId
  , IsBackend (..)
  , IsEffectuator (..)
  , GenericBackendFailure (..)
  )
  where

import           Control.Exception (Exception)
import           Data.Aeson (FromJSON)
import           Data.Text (Text)

import           Cardano.BM.Data.BackendKind
import           Cardano.BM.Data.LogItem
import           Cardano.BM.Data.Trace
import           Cardano.BM.Configuration (Configuration)

\end{code}
%endif

\subsubsection{BackendId}\label{code:BackendId}\index{BackendId}
A backend is identified by |BackendKind x Name|
\begin{code}
type BackendId = Text

\end{code}

\subsubsection{Accepts a |LogObject|}\label{code:IsEffectuator}\index{IsEffectuator}
Instances of this type class accept a |LogObject| and deal with it.
\begin{code}
class IsEffectuator t a where
    effectuate     :: t a -> LogObject a -> IO ()
    effectuatefrom :: forall s . (IsEffectuator s a) => t a -> LogObject a -> s a -> IO ()
    default effectuatefrom :: forall s . (IsEffectuator s a) => t a -> LogObject a -> s a -> IO ()
    effectuatefrom t a
t LogObject a
nli s a
_ = t a -> LogObject a -> IO ()
forall (t :: * -> *) a.
IsEffectuator t a =>
t a -> LogObject a -> IO ()
effectuate t a
t LogObject a
nli
    handleOverflow :: t a -> IO ()

\end{code}

\subsubsection{Declaration of a |Backend|}\label{code:IsBackend}\index{IsBackend}
A backend is life-cycle managed, thus can be |realize|d and |unrealize|d.
\begin{code}
class ( IsEffectuator t a
      , FromJSON a
      , Exception (BackendFailure t)
      ) => IsBackend t a where
    type BackendFailure t :: *
    type BackendFailure t = GenericBackendFailure
    bekind      :: t a -> BackendKind
    realize     :: Configuration -> IO (t a)
    realizefrom :: forall s . (IsEffectuator s a) => Configuration -> Trace IO a -> s a -> IO (t a)
    default realizefrom :: forall s . (IsEffectuator s a) => Configuration -> Trace IO a -> s a -> IO (t a)
    realizefrom Configuration
cfg Trace IO a
_ s a
_ = Configuration -> IO (t a)
forall (t :: * -> *) a. IsBackend t a => Configuration -> IO (t a)
realize Configuration
cfg
    unrealize   :: t a -> IO ()

\end{code}

\subsubsection{Backend}\label{code:Backend}\index{Backend}
This data structure for a backend defines its behaviour
as an |IsEffectuator| when processing an incoming message,
and as an |IsBackend| for unrealizing the backend.
\begin{code}
data Backend a = MkBackend
    { Backend a -> LogObject a -> IO ()
bEffectuate :: LogObject a -> IO ()
    , Backend a -> IO ()
bUnrealize  :: IO ()
    }

\end{code}

\subsubsection{GenericBackendFailure}\label{code:GenericBackendFailure}\index{GenericBackendFailure}
A default type for backend-specific failures, when they
wouldn't care to define their own.
\begin{code}
newtype GenericBackendFailure =
  GenericBackendFailure { GenericBackendFailure -> String
unGenericBackendFailure :: String }

instance Exception GenericBackendFailure
instance Show GenericBackendFailure where
  show :: GenericBackendFailure -> String
show GenericBackendFailure
x = String
"Generic backend failure: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> GenericBackendFailure -> String
unGenericBackendFailure GenericBackendFailure
x

\end{code}