batchd-0.1.1.0: src/Batchd/Client/Monad.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE RankNTypes #-}
-- | This module contains defintion of @Client@ data type and utility functions for it.
module Batchd.Client.Monad where
import Control.Monad.State
import Control.Exception
import qualified Data.Text.Lazy as TL
import Network.HTTP.Client
import System.Log.Heavy
import qualified System.Log.FastLogger as FL
import Batchd.Core.Common.Localize
import Batchd.Common.Types
import Batchd.Client.Config
import Batchd.Client.Types
import Batchd.Client.CmdLine
-- | Client state
data ClientState = ClientState {
csCmdline :: CmdLine -- ^ Parsed command line
, csConfig :: ClientConfig -- ^ Loaded configuration file
, csCredentials :: Maybe Credentials -- ^ Cached user credentials
, csAuthMethods :: Maybe [AuthMethod] -- ^ Cached list of authentication methods supported by server
, csLogger :: Maybe SpecializedLogger -- ^ Logging function
, csManager :: Maybe Manager -- ^ HTTP\/HTTPS manager
}
-- | Client monad
type Client a = StateT ClientState IO a
instance Localized (StateT ClientState IO) where
getLanguage = lift getLanguage
getTranslations = lift getTranslations
getContext = lift getContext
instance HasLogger (StateT ClientState IO) where
getLogger = do
mbLogger <- gets csLogger
case mbLogger of
Just logger -> return logger
Nothing -> fail "Logger is not initialized yet"
localLogger logger actions = do
oldLogger <- gets csLogger
modify $ \st -> st {csLogger = Just logger}
result <- actions
modify $ \st -> st {csLogger = oldLogger}
return result
-- | Execute actions within Client monad.
runClient :: ClientState -> Client a -> IO a
runClient st action =
evalStateT (withLogging (LoggingSettings logSettings) action) st
where
logSettings =
filtering [([], verbosity)] $
FastLoggerSettings logFormat (FL.LogStderr 0)
verbosity = logLevel $ cmdCommon $ csCmdline st
logFormat = "{level:~l}: {message}\n"
-- | Get manager base URL.
getBaseUrl :: Client String
getBaseUrl = do
cfg <- gets csConfig
opts <- gets csCmdline
liftIO $ getManagerUrl opts cfg
-- | Throw an exception.
throwC :: TL.Text -> Client a
throwC msg = lift $ throw $ ClientException msg
wrapClient :: (forall a. IO a -> IO a) -> Client b -> Client b
wrapClient wrapper actions = do
state <- get
(result, state') <- liftIO $ wrapper $ do
runStateT actions state
put state'
return result
-- | Catch an exception.
catchC :: Exception e
=> Client a
-> (e -> Client a)
-> Client a
catchC action handler = do
state <- get
(result, state') <- liftIO $ runStateT action state `catch` \e ->
runStateT (handler e) state
put state'
return result