imm-1.0.0.0: src/lib/Imm/Boot.hs
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeOperators #-}
module Imm.Boot (imm) where
-- {{{ Imports
import qualified Imm.Core as Core
import Imm.Database.FeedTable as Database
import Imm.Database as Database
import Imm.Dyre as Dyre
import Imm.Feed
import Imm.HTTP as HTTP
import Imm.Hooks
import Imm.Logger as Logger
import Imm.Options as Options hiding(logLevel)
import Imm.Prelude
import Control.Comonad.Cofree
import Control.Monad.Trans.Free
import System.IO (hFlush)
-- }}}
-- | Main function, meant to be used in your personal configuration file,
-- by default located at @$XDG_CONFIG_HOME\/imm\/imm.hs@.
--
-- For more information about the dynamic reconfiguration system, please consult "Config.Dyre".
--
-- Here is an example:
--
-- > import Imm.Boot
-- > import Imm.Database.JsonFile
-- > import Imm.Feed
-- > import Imm.Hooks.SendMail
-- > import Imm.HTTP.Simple
-- > import Imm.Logger.Simple
-- >
-- > main :: IO ()
-- > main = do
-- > logger <- defaultLogger
-- > manager <- defaultManager
-- > database <- defaultDatabase
-- >
-- > imm (mkCoHttpClient, manager) (mkCoDatabase, database) (mkCoLogger, logger) (mkCoHooks, sendmail)
-- >
-- > sendmail :: SendMailSettings
-- > sendmail = SendMailSettings smtpServer formatMail
-- >
-- > formatMail :: FormatMail
-- > formatMail = FormatMail
-- > (\a b -> (defaultFormatFrom a b) { addressEmail = "user@host" } )
-- > defaultFormatSubject
-- > defaultFormatBody
-- > (\_ _ -> [Address Nothing "user@host"])
-- >
-- > smtpServer :: Feed -> FeedElement -> SMTPServer
-- > smtpServer _ _ = SMTPServer
-- > (Just $ Authentication PLAIN "user" "password")
-- > (StartTls "smtp.host" defaultSettingsSMTPSTARTTLS)
imm :: (a -> CoHttpClientF IO a, a) -- ^ HTTP client interpreter (cf "Imm.HTTP")
-> (b -> CoDatabaseF' IO b, b) -- ^ Database interpreter (cf "Imm.Database")
-> (c -> CoLoggerF IO c, c) -- ^ Logger interpreter (cf "Imm.Logger")
-> (d -> CoHooksF IO d, d) -- ^ Hooks interpreter (cf "Imm.Hooks")
-> IO ()
imm coHttpClient coDatabase coLogger coHooks = void $ do
options <- parseOptions
Dyre.wrap (optionDyreMode options) realMain (optionCommand options, optionLogLevel options, coiter next start)
where (next, start) = mkCoImm coHttpClient coDatabase coLogger coHooks
realMain :: (MonadIO m, PairingM (CoImmF m) ImmF m, MonadCatch m)
=> (Command, LogLevel, Cofree (CoImmF m) a) -> m ()
realMain (command, logLevel, interpreter) = void $ interpret (\_ b -> return b) interpreter $ do
setLogLevel logLevel
logDebug $ "Executing: " <> show (pretty command)
handleAll (logError . fromString . displayException) $ case command of
Check t -> Core.check =<< resolveTarget ByPassConfirmation t
Import -> Core.importOPML
Read t -> mapM_ Database.markAsRead =<< resolveTarget AskConfirmation t
Run t -> Core.run =<< resolveTarget ByPassConfirmation t
Show t -> Core.showFeed =<< resolveTarget ByPassConfirmation t
ShowVersion -> Core.printVersions
Subscribe u c -> Core.subscribe u c
Unread t -> mapM_ Database.markAsUnread =<< resolveTarget AskConfirmation t
Unsubscribe t -> Database.deleteList FeedTable =<< resolveTarget AskConfirmation t
_ -> return ()
Database.commit FeedTable
return ()
-- * DSL/interpreter model
type CoImmF m = CoHttpClientF m :*: CoDatabaseF' m :*: CoLoggerF m :*: CoHooksF m
type ImmF = HttpClientF :+: DatabaseF' :+: LoggerF :+: HooksF
mkCoImm :: (Functor m)
=> (a -> CoHttpClientF m a, a) -> (b -> CoDatabaseF' m b, b) -> (c -> CoLoggerF m c, c) -> (d -> CoHooksF m d, d)
-> ((a ::: b ::: c ::: d) -> CoImmF m (a ::: b ::: c ::: d), a ::: b ::: c ::: d)
mkCoImm (coHttpClient, a) (coDatabase, b) (coLogger, c) (coHooks, d) =
(coHttpClient *:* coDatabase *:* coLogger *:* coHooks, a >: b >: c >: d)
-- * Util
data SafeGuard = AskConfirmation | ByPassConfirmation
deriving(Eq, Show)
data InterruptedException = InterruptedException deriving(Eq, Show)
instance Exception InterruptedException where
displayException _ = "Process interrupted"
promptConfirm :: (MonadIO m, MonadThrow m) => Text -> m ()
promptConfirm s = do
hPut stdout $ s <> " Confirm [Y/n] "
io $ hFlush stdout
x <- getLine
when (x /= ("" :: Text) && x /= ("Y" :: Text)) $ throwM InterruptedException
resolveTarget :: (MonadIO m, MonadThrow m, Functor f, MonadFree f m, DatabaseF' :<: f, LoggerF :<: f)
=> SafeGuard -> Maybe Core.FeedRef -> m [FeedID]
resolveTarget s Nothing = do
result <- keys <$> Database.fetchAll FeedTable
when (s == AskConfirmation) . promptConfirm $ "This will affect " <> show (length result) <> " feeds."
return result
resolveTarget _ (Just (FeedRef (Left i))) = do
result <- fst . (!! i) . mapToList <$> Database.fetchAll FeedTable
-- logInfo $ "Target(s): " <> show (pretty result)
return $ singleton result
resolveTarget _ (Just (FeedRef (Right uri))) = return [FeedID uri]