{-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} -- | -- = Getting started -- -- == Dynamic reconfiguration -- -- This program is dynamically configured using the library. -- -- You may want to check out to know how to get started. -- -- Your personal configuration is located at @$XDG_CONFIG_HOME\/imm\/imm.hs@. -- -- == Handle pattern -- -- The behavior of this program can be customized through the [Handle pattern](https://jaspervdj.be/posts/2018-03-08-handle-pattern.html). module Imm.Boot (imm) where -- {{{ Imports import qualified Imm.Core as Core import Imm.Database as Database import Imm.Database.FeedTable as Database import Imm.Dyre as Dyre import Imm.Feed import Imm.Hooks as Hooks import Imm.HTTP as HTTP import Imm.Logger as Logger import Imm.Options as Options hiding (logLevel) import Imm.Prelude import Imm.Pretty import Imm.XML as XML import Data.Conduit.Combinators (stdin) import System.IO (hFlush) -- }}} -- | Main function, meant to be used in your personal configuration file. -- -- Here is an example: -- -- > import Imm.Boot -- > import Imm.Database.JsonFile as Database -- > import Imm.Feed -- > import Imm.Hooks.SendMail as Hooks -- > import Imm.HTTP.Simple as HTTP -- > import Imm.Logger.Simple as Logger -- > import Imm.XML.Conduit as XML -- > -- > main :: IO () -- > main = do -- > logger <- Logger.mkHandle <$> defaultLogger -- > database <- Database.mkHandle <$> defaultDatabase -- > httpClient <- HTTP.mkHandle <$> defaultManager -- > -- > imm logger database httpClient hooks xmlParser -- > -- > xmlParser :: XML.Handle IO -- > xmlParser = XML.mkHandle defaultXmlParser -- > -- > hooks :: Hooks.Handle IO -- > hooks = Hooks.mkHandle $ 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 :: Logger.Handle IO -> Database.Handle IO FeedTable -> HTTP.Handle IO -> Hooks.Handle IO -> XML.Handle IO -> IO () imm logger database httpClient hooks xmlParser = void $ do options <- parseOptions Dyre.wrap (optionDyreMode options) realMain (optionCommand options, optionLogLevel options, optionColorizeLogs options, logger, database, httpClient, hooks, xmlParser) realMain :: (Command, LogLevel, Bool, Logger.Handle IO, Database.Handle IO FeedTable, HTTP.Handle IO, Hooks.Handle IO, XML.Handle IO) -> IO () realMain (command, logLevel, enableColors, logger, database, httpClient, hooks, xmlParser) = void $ do setColorizeLogs logger enableColors setLogLevel logger logLevel log logger Debug . ("Dynamic reconfiguration settings:" <++>) . indent 2 =<< Dyre.describePaths log logger Debug $ "Executing: " <> pretty command log logger Debug . ("Using database:" <++>) . indent 2 =<< _describeDatabase database handleAny (log logger Error . pretty . displayException) $ case command of Check t -> Core.check logger database httpClient xmlParser =<< resolveTarget database ByPassConfirmation t Help -> liftBase $ putStrLn helpString Import -> Core.importOPML logger database stdin Read t -> mapM_ (Database.markAsRead logger database) =<< resolveTarget database AskConfirmation t Run t -> Core.run logger database httpClient hooks xmlParser =<< resolveTarget database ByPassConfirmation t Show t -> Core.showFeed logger database =<< resolveTarget database ByPassConfirmation t ShowVersion -> Core.printVersions Subscribe u c -> Core.subscribe logger database u c Unread t -> mapM_ (Database.markAsUnread logger database) =<< resolveTarget database AskConfirmation t Unsubscribe t -> Database.deleteList logger database =<< resolveTarget database AskConfirmation t _ -> return () Database.commit logger database flushLogs logger -- * Util data SafeGuard = AskConfirmation | ByPassConfirmation deriving(Eq, Read, Show) data InterruptedException = InterruptedException deriving(Eq, Read, Show) instance Exception InterruptedException where displayException _ = "Process interrupted" promptConfirm :: Text -> IO () promptConfirm s = do putStr $ s <> " Confirm [Y/n] " hFlush stdout x <- getLine unless (null x || x == ("Y" :: Text)) $ throwM InterruptedException resolveTarget :: MonadBase IO m => MonadThrow m => Database.Handle m FeedTable -> SafeGuard -> Maybe Core.FeedRef -> m [FeedID] resolveTarget database s Nothing = do result <- keys <$> Database.fetchAll database when (s == AskConfirmation) $ liftBase $ promptConfirm $ "This will affect " <> show (length result) <> " feeds." return result resolveTarget database _ (Just (ByUID i)) = do result <- fst . (!! (i-1)) . mapToList <$> Database.fetchAll database -- log logger Info $ "Target(s): " <> show (pretty result) return $ singleton result resolveTarget _ _ (Just (ByURI uri)) = return [FeedID uri]