module Imm.Boot (imm) where
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 Imm.Pretty
import Imm.XML
import Control.Comonad.Cofree
import Control.Monad.Trans.Free
import Data.Functor.Product
import Data.Functor.Sum
import System.IO (hFlush)
imm :: (a -> CoHttpClientF IO a, a)
-> (b -> CoDatabaseF' IO b, b)
-> (c -> CoLoggerF IO c, c)
-> (d -> CoHooksF IO d, d)
-> (e -> CoXmlParserF IO e, e)
-> IO ()
imm coHttpClient coDatabase coLogger coHooks coXmlParser = void $ do
options <- parseOptions
Dyre.wrap (optionDyreMode options) realMain (optionCommand options, optionLogLevel options, optionColorizeLogs options, coiter next start)
where (next, start) = mkCoImm coHttpClient coDatabase coLogger coHooks coXmlParser
realMain :: (MonadIO m, PairingM (CoImmF m) ImmF m, MonadCatch m)
=> (Command, LogLevel, Bool, Cofree (CoImmF m) a) -> m ()
realMain (command, logLevel, colorizeLogs, interpreter) = void $ interpret (\_ b -> return b) interpreter $ do
setColorizeLogs colorizeLogs
setLogLevel logLevel
logDebug . ("Dynamic reconfiguration settings:" <++>) . indent 2 =<< Dyre.describePaths
logDebug $ "Executing: " <> pretty command
logDebug . ("Using database:" <++>) . indent 2 =<< describeDatabase FeedTable
handleAny (logError . textual . 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
flushLogs
type CoImmF m = Product (CoHttpClientF m)
(Product (CoDatabaseF' m)
(Product (CoLoggerF m)
(Product (CoHooksF m) (CoXmlParserF m)
)))
type ImmF = Sum HttpClientF (Sum DatabaseF' (Sum LoggerF (Sum HooksF XmlParserF)))
mkCoImm :: (Functor m)
=> (a -> CoHttpClientF m a, a)
-> (b -> CoDatabaseF' m b, b)
-> (c -> CoLoggerF m c, c)
-> (d -> CoHooksF m d, d)
-> (e -> CoXmlParserF m e, e)
-> ((a ::: b ::: c ::: d ::: e) -> CoImmF m (a ::: b ::: c ::: d ::: e), a ::: b ::: c ::: d ::: e)
mkCoImm (coHttpClient, a) (coDatabase, b) (coLogger, c) (coHooks, d) (coXmlParser, e) =
(coHttpClient *: coDatabase *: coLogger *: coHooks *: coXmlParser, a +: b +: c +: d +: e)
data SafeGuard = AskConfirmation | ByPassConfirmation
deriving(Eq, Read, Show)
data InterruptedException = InterruptedException deriving(Eq, Read, 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
unless (null x || x == ("Y" :: Text)) $ throwM InterruptedException
resolveTarget :: (MonadIO m, MonadThrow m, MonadFree f m, DatabaseF' :<: 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 (ByUID i)) = do
result <- fst . (!! i) . mapToList <$> Database.fetchAll FeedTable
return $ singleton result
resolveTarget _ (Just (ByURI uri)) = return [FeedID uri]