vimeta-0.3.0.0: src/Vimeta/Core/Vimeta.hs
-- |
--
-- Copyright:
-- This file is part of the package vimeta. It is subject to the
-- license terms in the LICENSE file found in the top-level
-- directory of this distribution and at:
--
-- https://github.com/pjones/vimeta
--
-- No part of this package, including this file, may be copied,
-- modified, propagated, or distributed except according to the terms
-- contained in the LICENSE file.
--
-- License: BSD-2-Clause
module Vimeta.Core.Vimeta
( Vimeta (..),
Context (..),
MonadIO,
throwError,
runIO,
runIOE,
tmdb,
verbose,
execVimetaWithContext,
execVimeta,
runVimeta,
)
where
import Byline (BylineT, MonadByline, runBylineT)
import Control.Monad.Catch
import Control.Monad.Except
import qualified Data.Text.IO as Text
import GHC.IO.Encoding (setLocaleEncoding, utf8)
import qualified Network.API.TheMovieDB as TMDb
import Network.HTTP.Client (Manager, newManager)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import Vimeta.Core.Cache
import Vimeta.Core.Config
data Context = Context
{ ctxManager :: Manager,
ctxConfig :: Config,
ctxTMDBCfg :: TMDb.Configuration,
ctxVerboseH :: Handle
}
newtype Vimeta m a = Vimeta
{unV :: ReaderT Context (BylineT (ExceptT String m)) a}
deriving
( Functor,
Applicative,
Monad,
MonadIO,
MonadReader Context,
MonadError String,
MonadByline
)
runIO :: (MonadIO m) => IO a -> Vimeta m a
runIO io = liftIO (try io) >>= sinkIO
where
sinkIO :: (Monad m) => Either SomeException a -> Vimeta m a
sinkIO (Left e) = throwError (show e)
sinkIO (Right a) = return a
runIOE :: (MonadIO m) => IO (Either String a) -> Vimeta m a
runIOE io = runIO io >>= either (throwError . show) return
-- | Run a 'TheMovieDB' operation.
tmdb :: (MonadIO m) => TMDb.TheMovieDB a -> Vimeta m a
tmdb t = do
context' <- ask
let manager = ctxManager context'
key = configTMDBKey (ctxConfig context')
settings = TMDb.defaultSettings key
result <- liftIO (TMDb.runTheMovieDBWithManager manager settings t)
case result of
Left e -> throwError (show e)
Right r -> return r
verbose :: (MonadIO m) => Text -> Vimeta m ()
verbose msg = do
context <- ask
let okay =
configVerbose (ctxConfig context)
|| configDryRun (ctxConfig context)
when okay $ liftIO $ Text.hPutStrLn (ctxVerboseH context) msg
loadTMDBConfig ::
(MonadIO m) =>
Manager ->
TMDb.Settings ->
ExceptT String m TMDb.Configuration
loadTMDBConfig manager settings = do
result <-
cacheTMDBConfig
( liftIO $ TMDb.runTheMovieDBWithManager manager settings TMDb.config
)
case result of
Left e -> throwError (show e)
Right c -> return c
-- | Very primitive way of running a 'Vimeta' value with the given 'Context'.
-- Mostly useful for running vimeta action within another vimeta
-- action.
execVimetaWithContext ::
(MonadIO m, MonadMask m) =>
Context ->
Vimeta m a ->
m (Either String a)
execVimetaWithContext context vimeta =
unV vimeta
& (`runReaderT` context)
& runBylineT
& (>>= maybe (throwError "EOF") pure)
& runExceptT
-- | Force the current process to use UTF-8 for output.
forceUTF8 :: IO ()
forceUTF8 = setLocaleEncoding utf8
-- | Run a 'Vimeta' operation after loading the configuration file
-- from disk.
execVimeta ::
(MonadIO m, MonadMask m) =>
-- | Modify configuration before running.
(Config -> Config) ->
-- | The Vimeta value to execute.
Vimeta m a ->
-- | The result.
m (Either String a)
execVimeta cf vimeta = runExceptT $ do
liftIO forceUTF8
config <- cf <$> readConfig
manager <- liftIO $ newManager tlsManagerSettings
tc <- loadTMDBConfig manager (TMDb.defaultSettings (configTMDBKey config))
ExceptT $ execVimetaWithContext (Context manager config tc stdout) vimeta
-- | Simple wrapper around 'execVimeta'.
runVimeta :: (MonadIO m, MonadMask m) => Vimeta m a -> m (Either String a)
runVimeta = execVimeta id