{-# OPTIONS_GHC -fno-warn-orphans #-} {-| Module : Game.GoreAndAsh.Logging.Module Description : Monad transformer for logging module Copyright : (c) Anton Gushcha, 2015-2016 License : BSD3 Maintainer : ncrashed@gmail.com Stability : experimental Portability : POSIX Contains description of logging monad transformer and instance for 'GameModule' class. -} module Game.GoreAndAsh.Logging.Module( LoggingT(..) ) where import Control.Monad.Base import Control.Monad.Catch import Control.Monad.Error.Class import Control.Monad.Extra (whenJust) import Control.Monad.Fix import Control.Monad.State.Strict import Control.Monad.Trans.Resource import Data.Proxy import Data.Text (Text) import qualified Data.Sequence as S import qualified Data.Text.IO as T import qualified System.IO as IO import Game.GoreAndAsh import Game.GoreAndAsh.Logging.State -- | Monad transformer of logging core module. -- -- [@s@] - State of next core module in modules chain; -- -- [@m@] - Next monad in modules monad stack; -- -- [@a@] - Type of result value; -- -- How to embed module: -- -- @ -- type AppStack = ModuleStack [LoggingT, ... other modules ... ] IO -- -- newtype AppMonad a = AppMonad (AppStack a) -- deriving (Functor, Applicative, Monad, MonadFix, MonadIO, LoggingMonad) -- @ -- -- The module is pure within first phase (see 'ModuleStack' docs) and could be used -- with 'Identity' end monad. newtype LoggingT s m a = LoggingT { runLoggingT :: StateT (LoggingState s) m a } deriving (Functor, Applicative, Monad, MonadState (LoggingState s), MonadFix, MonadTrans, MonadIO, MonadThrow, MonadCatch, MonadMask, MonadError e) instance MonadBase IO m => MonadBase IO (LoggingT s m) where liftBase = LoggingT . liftBase instance MonadResource m => MonadResource (LoggingT s m) where liftResourceT = LoggingT . liftResourceT instance GameModule m s => GameModule (LoggingT s m) (LoggingState s) where type ModuleState (LoggingT s m) = LoggingState s runModule (LoggingT m) s = do ((a, s'), nextState) <- runModule (runStateT m s) (loggingNextState s) printAllMsgs s' return (a, s' { loggingMsgs = S.empty , loggingNextState = nextState }) where printAllMsgs ls@LoggingState{..} = do mapM_ (uncurry $ consoleOutput ls) loggingMsgs mapM_ (uncurry $ fileOutput ls) loggingMsgs newModuleState = emptyLoggingState <$> newModuleState withModule _ = withModule (Proxy :: Proxy m) cleanupModule LoggingState{..} = case loggingFile of Nothing -> return () Just h -> IO.hClose h -- | Output given message to logging file if allowed fileOutput :: MonadIO m => LoggingState s -> LoggingLevel -> Text -> m () fileOutput ls ll msg = when (filterLogMessage ls ll LoggingFile) $ whenJust (loggingFile ls) $ \h -> liftIO $ T.hPutStrLn h msg -- | Output given message to console if allowed consoleOutput :: MonadIO m => LoggingState s -> LoggingLevel -> Text -> m () consoleOutput ls ll msg = when (filterLogMessage ls ll LoggingConsole) $ liftIO $ T.putStrLn msg