packages feed

imm 1.3.0.0 → 1.4.0.0

raw patch · 20 files changed

+317/−375 lines, 20 filesdep +monad-controldep −transformersPVP ok

version bump matches the API change (PVP)

Dependencies added: monad-control

Dependencies removed: transformers

API changes (from Hackage documentation)

- Imm.Boot: Modules :: httpClient -> databaseClient -> logger -> hooks -> xmlParser -> Modules httpClient databaseClient logger hooks xmlParser
- Imm.Boot: [_databaseClient] :: Modules httpClient databaseClient logger hooks xmlParser -> databaseClient
- Imm.Boot: [_hooks] :: Modules httpClient databaseClient logger hooks xmlParser -> hooks
- Imm.Boot: [_httpClient] :: Modules httpClient databaseClient logger hooks xmlParser -> httpClient
- Imm.Boot: [_logger] :: Modules httpClient databaseClient logger hooks xmlParser -> logger
- Imm.Boot: [_xmlParser] :: Modules httpClient databaseClient logger hooks xmlParser -> xmlParser
- Imm.Boot: data Modules httpClient databaseClient logger hooks xmlParser
- Imm.Boot: data ModulesM m
- Imm.Boot: instance (Control.Monad.Catch.MonadThrow m, Imm.Database.MonadDatabase Imm.Database.FeedTable.FeedTable (Control.Monad.Trans.Reader.ReaderT b m)) => Imm.Database.MonadDatabase Imm.Database.FeedTable.FeedTable (Control.Monad.Trans.Reader.ReaderT (Imm.Boot.Modules a b c d e) m)
- Imm.Boot: instance (Control.Monad.Catch.MonadThrow m, Imm.HTTP.MonadHttpClient (Control.Monad.Trans.Reader.ReaderT a m)) => Imm.HTTP.MonadHttpClient (Control.Monad.Trans.Reader.ReaderT (Imm.Boot.Modules a b c d e) m)
- Imm.Boot: instance (Control.Monad.Catch.MonadThrow m, Imm.XML.MonadXmlParser (Control.Monad.Trans.Reader.ReaderT e m)) => Imm.XML.MonadXmlParser (Control.Monad.Trans.Reader.ReaderT (Imm.Boot.Modules a b c d e) m)
- Imm.Boot: instance (Control.Monad.IO.Class.MonadIO m, Imm.Logger.MonadLog (Control.Monad.Trans.Reader.ReaderT c m)) => Imm.Logger.MonadLog (Control.Monad.Trans.Reader.ReaderT (Imm.Boot.Modules a b c d e) m)
- Imm.Boot: instance (GHC.Base.Monad m, Imm.Hooks.MonadImm (Control.Monad.Trans.Reader.ReaderT d m)) => Imm.Hooks.MonadImm (Control.Monad.Trans.Reader.ReaderT (Imm.Boot.Modules a b c d e) m)
- Imm.Boot: instance GHC.Exception.Exception Imm.Boot.InterruptedException
- Imm.Boot: mkModulesM :: (MonadXmlParser (ReaderT e m), MonadImm (ReaderT d m), MonadLog (ReaderT c m), MonadDatabase FeedTable (ReaderT b m), MonadHttpClient (ReaderT a m)) => a -> b -> c -> d -> e -> ModulesM m
- Imm.Database: _commit :: MonadDatabase t m => t -> m ()
- Imm.Database: _deleteList :: MonadDatabase t m => t -> [Key t] -> m ()
- Imm.Database: _describeDatabase :: MonadDatabase t m => t -> m (Doc a)
- Imm.Database: _fetchAll :: MonadDatabase t m => t -> m (Map (Key t) (Entry t))
- Imm.Database: _fetchList :: MonadDatabase t m => t -> [Key t] -> m (Map (Key t) (Entry t))
- Imm.Database: _insertList :: MonadDatabase t m => t -> [(Key t, Entry t)] -> m ()
- Imm.Database: _purge :: MonadDatabase t m => t -> m ()
- Imm.Database: _update :: MonadDatabase t m => t -> Key t -> (Entry t -> Entry t) -> m ()
- Imm.Database: class MonadThrow m => MonadDatabase t m
- Imm.Database: instance (Imm.Database.Table t, GHC.Show.Show (Imm.Database.Key t), GHC.Show.Show (Imm.Database.Entry t), Data.Text.Prettyprint.Doc.Internal.Pretty (Imm.Database.Key t), Data.Typeable.Internal.Typeable t) => GHC.Exception.Exception (Imm.Database.DatabaseException t)
- Imm.Database.JsonFile: instance (Imm.Database.Table t, Data.Aeson.Types.FromJSON.FromJSON (Imm.Database.Key t), Data.Aeson.Types.FromJSON.FromJSON (Imm.Database.Entry t), Data.Aeson.Types.ToJSON.ToJSON (Imm.Database.Key t), Data.Aeson.Types.ToJSON.ToJSON (Imm.Database.Entry t)) => Imm.Database.MonadDatabase t (Control.Monad.Trans.Reader.ReaderT (GHC.MVar.MVar (Imm.Database.JsonFile.JsonFileDatabase t)) GHC.Types.IO)
- Imm.Database.JsonFile: instance GHC.Exception.Exception Imm.Database.JsonFile.JsonException
- Imm.HTTP: class MonadThrow m => MonadHttpClient m
- Imm.HTTP: httpGet :: MonadHttpClient m => URI -> m LByteString
- Imm.HTTP.Simple: instance Imm.HTTP.MonadHttpClient (Control.Monad.Trans.Reader.ReaderT Network.HTTP.Client.Types.Manager GHC.Types.IO)
- Imm.Hooks: class Monad m => MonadImm m
- Imm.Hooks: processNewElement :: MonadImm m => Feed -> FeedElement -> m ()
- Imm.Hooks.Dummy: instance Imm.Hooks.MonadImm (Control.Monad.Trans.Reader.ReaderT Imm.Hooks.Dummy.DummyHooks GHC.Types.IO)
- Imm.Hooks.SendMail: instance Imm.Hooks.MonadImm (Control.Monad.Trans.Reader.ReaderT Imm.Hooks.SendMail.SendMailSettings GHC.Types.IO)
- Imm.Hooks.WriteFile: instance Imm.Hooks.MonadImm (Control.Monad.Trans.Reader.ReaderT Imm.Hooks.WriteFile.WriteFileSettings GHC.Types.IO)
- Imm.Logger: class Monad m => MonadLog m
- Imm.Logger: flushLogs :: MonadLog m => m ()
- Imm.Logger: getLogLevel :: MonadLog m => m LogLevel
- Imm.Logger: log :: MonadLog m => LogLevel -> Doc AnsiStyle -> m ()
- Imm.Logger: logDebug :: MonadLog m => Doc AnsiStyle -> m ()
- Imm.Logger: logError :: MonadLog m => Doc AnsiStyle -> m ()
- Imm.Logger: logInfo :: MonadLog m => Doc AnsiStyle -> m ()
- Imm.Logger: logWarning :: MonadLog m => Doc AnsiStyle -> m ()
- Imm.Logger: setColorizeLogs :: MonadLog m => Bool -> m ()
- Imm.Logger: setLogLevel :: MonadLog m => LogLevel -> m ()
- Imm.Logger.Simple: instance Imm.Logger.MonadLog (Control.Monad.Trans.Reader.ReaderT (GHC.MVar.MVar Imm.Logger.Simple.LoggerSettings) GHC.Types.IO)
- Imm.XML: class MonadThrow m => MonadXmlParser m
- Imm.XML: parseXml :: MonadXmlParser m => URI -> LByteString -> m Feed
- Imm.XML.Conduit: instance (Control.Monad.IO.Class.MonadIO m, Control.Monad.Catch.MonadCatch m) => Imm.XML.MonadXmlParser (Control.Monad.Trans.Reader.ReaderT Imm.XML.Conduit.XmlParser m)
+ Imm: Atom :: AtomFeed -> Feed
+ Imm: AtomElement :: AtomEntry -> FeedElement
+ Imm: ByUID :: Int -> FeedRef
+ Imm: ByURI :: URI -> FeedRef
+ Imm: Debug :: LogLevel
+ Imm: Error :: LogLevel
+ Imm: Info :: LogLevel
+ Imm: NotCommitted :: t -> DatabaseException t
+ Imm: NotDeleted :: t -> [Key t] -> DatabaseException t
+ Imm: NotFound :: t -> [Key t] -> DatabaseException t
+ Imm: NotInserted :: t -> [(Key t, Entry t)] -> DatabaseException t
+ Imm: NotPurged :: t -> DatabaseException t
+ Imm: NotUpdated :: t -> Key t -> DatabaseException t
+ Imm: Rss :: RssDocument '[ContentModule, DublinCoreModule] -> Feed
+ Imm: RssElement :: RssItem '[ContentModule, DublinCoreModule] -> FeedElement
+ Imm: UnableFetchAll :: t -> DatabaseException t
+ Imm: Warning :: LogLevel
+ Imm: _commit :: Handle m t -> m ()
+ Imm: _deleteList :: Handle m t -> [Key t] -> m ()
+ Imm: _describeDatabase :: Handle m t -> forall a. m (Doc a)
+ Imm: _fetchAll :: Handle m t -> m (Map (Key t) (Entry t))
+ Imm: _fetchList :: Handle m t -> [Key t] -> m (Map (Key t) (Entry t))
+ Imm: _insertList :: Handle m t -> [(Key t, Entry t)] -> m ()
+ Imm: _purge :: Handle m t -> m ()
+ Imm: _update :: Handle m t -> Key t -> (Entry t -> Entry t) -> m ()
+ Imm: check :: (MonadAsync m, MonadCatch m) => Handle m -> Handle m FeedTable -> Handle m -> Handle m -> [FeedID] -> m ()
+ Imm: class (Ord (Key t), Show (Key t), Show (Entry t), Typeable t, Show t, Pretty t, Pretty (Key t)) => Table t where {
+ Imm: commit :: Monad m => Handle m -> Handle m t -> m ()
+ Imm: data DatabaseException t
+ Imm: data Feed
+ Imm: data FeedElement
+ Imm: data FeedRef
+ Imm: data LogLevel
+ Imm: delete :: Monad m => Handle m -> Handle m t -> Key t -> m ()
+ Imm: deleteList :: Monad m => Handle m -> Handle m t -> [Key t] -> m ()
+ Imm: fetch :: Monad m => Table t => MonadThrow m => Handle m t -> Key t -> m (Entry t)
+ Imm: fetchAll :: Monad m => Handle m t -> m (Map (Key t) (Entry t))
+ Imm: fetchList :: Monad m => Handle m t -> [Key t] -> m (Map (Key t) (Entry t))
+ Imm: flushLogs :: Handle m -> m ()
+ Imm: get :: Monad m => Handle m -> Handle m -> URI -> m LByteString
+ Imm: getContent :: FeedElement -> Text
+ Imm: getDate :: FeedElement -> Maybe UTCTime
+ Imm: getElements :: Feed -> [FeedElement]
+ Imm: getFeedTitle :: Feed -> Text
+ Imm: getHashes :: FeedElement -> [Int]
+ Imm: getLogLevel :: Handle m -> m LogLevel
+ Imm: getTitle :: FeedElement -> Text
+ Imm: httpGet :: Handle m -> URI -> m LByteString
+ Imm: imm :: Handle IO -> Handle IO FeedTable -> Handle IO -> Handle IO -> Handle IO -> IO ()
+ Imm: importOPML :: MonadCatch m => Handle m -> Handle m FeedTable -> ConduitT () ByteString m () -> m ()
+ Imm: insert :: Monad m => Handle m -> Handle m t -> Key t -> Entry t -> m ()
+ Imm: insertList :: Monad m => Handle m -> Handle m t -> [(Key t, Entry t)] -> m ()
+ Imm: log :: Handle m -> LogLevel -> Doc AnsiStyle -> m ()
+ Imm: onNewElement :: Monad m => Handle m -> Handle m -> Feed -> FeedElement -> m ()
+ Imm: prettyElement :: FeedElement -> Doc a
+ Imm: printVersions :: MonadBase IO m => m ()
+ Imm: processNewElement :: Handle m -> Feed -> FeedElement -> m ()
+ Imm: purge :: Monad m => Handle m -> Handle m t -> m ()
+ Imm: rep :: Table t => t
+ Imm: run :: (MonadTime m, MonadAsync m, MonadCatch m) => Handle m -> Handle m FeedTable -> Handle m -> Handle m -> Handle m -> [FeedID] -> m ()
+ Imm: setColorizeLogs :: Handle m -> Bool -> m ()
+ Imm: setLogLevel :: Handle m -> LogLevel -> m ()
+ Imm: showFeed :: MonadThrow m => Handle m -> Handle m FeedTable -> [FeedID] -> m ()
+ Imm: subscribe :: MonadCatch m => Handle m -> Handle m FeedTable -> URI -> Set Text -> m ()
+ Imm: table :: Table t => Handle m t -> t
+ Imm: type family Entry t :: *;
+ Imm: update :: Monad m => Handle m t -> Key t -> (Entry t -> Entry t) -> m ()
+ Imm: }
+ Imm.Boot: instance GHC.Exception.Type.Exception Imm.Boot.InterruptedException
+ Imm.Database: Handle :: (forall a. m (Doc a)) -> ([Key t] -> m (Map (Key t) (Entry t))) -> m (Map (Key t) (Entry t)) -> (Key t -> (Entry t -> Entry t) -> m ()) -> ([(Key t, Entry t)] -> m ()) -> ([Key t] -> m ()) -> m () -> m () -> Handle m t
+ Imm.Database: [_commit] :: Handle m t -> m ()
+ Imm.Database: [_deleteList] :: Handle m t -> [Key t] -> m ()
+ Imm.Database: [_describeDatabase] :: Handle m t -> forall a. m (Doc a)
+ Imm.Database: [_fetchAll] :: Handle m t -> m (Map (Key t) (Entry t))
+ Imm.Database: [_fetchList] :: Handle m t -> [Key t] -> m (Map (Key t) (Entry t))
+ Imm.Database: [_insertList] :: Handle m t -> [(Key t, Entry t)] -> m ()
+ Imm.Database: [_purge] :: Handle m t -> m ()
+ Imm.Database: [_update] :: Handle m t -> Key t -> (Entry t -> Entry t) -> m ()
+ Imm.Database: data Handle m t
+ Imm.Database: instance (Imm.Database.Table t, GHC.Show.Show (Imm.Database.Key t), GHC.Show.Show (Imm.Database.Entry t), Data.Text.Prettyprint.Doc.Internal.Pretty (Imm.Database.Key t), Data.Typeable.Internal.Typeable t) => GHC.Exception.Type.Exception (Imm.Database.DatabaseException t)
+ Imm.Database: rep :: Table t => t
+ Imm.Database: table :: Table t => Handle m t -> t
+ Imm.Database.JsonFile: instance GHC.Exception.Type.Exception Imm.Database.JsonFile.JsonException
+ Imm.Database.JsonFile: mkHandle :: (Table t, FromJSON (Key t), FromJSON (Entry t), ToJSON (Key t), ToJSON (Entry t), MonadBase IO m) => MVar (JsonFileDatabase t) -> Handle m t
+ Imm.HTTP: Handle :: (URI -> m LByteString) -> Handle m
+ Imm.HTTP: [httpGet] :: Handle m -> URI -> m LByteString
+ Imm.HTTP: newtype Handle m
+ Imm.HTTP.Simple: mkHandle :: MonadBase IO m => Manager -> Handle m
+ Imm.Hooks: Handle :: (Feed -> FeedElement -> m ()) -> Handle m
+ Imm.Hooks: [processNewElement] :: Handle m -> Feed -> FeedElement -> m ()
+ Imm.Hooks: newtype Handle m
+ Imm.Hooks.Dummy: mkHandle :: MonadThrow m => Handle m
+ Imm.Hooks.SendMail: AUTH :: AuthType -> UserName -> Password -> Command
+ Imm.Hooks.SendMail: Address :: Maybe Text -> Text -> Address
+ Imm.Hooks.SendMail: BadSequence :: Response
+ Imm.Hooks.SendMail: Base64 :: Encoding
+ Imm.Hooks.SendMail: Boundary :: Text -> Boundary
+ Imm.Hooks.SendMail: CRAM_MD5 :: AuthType
+ Imm.Hooks.SendMail: CannotVerify :: Response
+ Imm.Hooks.SendMail: CommandNotImplemented :: Response
+ Imm.Hooks.SendMail: DATA :: ByteString -> Command
+ Imm.Hooks.SendMail: EHLO :: String -> Command
+ Imm.Hooks.SendMail: EXPN :: String -> Command
+ Imm.Hooks.SendMail: ErrorInProcessing :: Response
+ Imm.Hooks.SendMail: ExceededStorage :: Response
+ Imm.Hooks.SendMail: HELO :: String -> Command
+ Imm.Hooks.SendMail: HELP :: String -> Command
+ Imm.Hooks.SendMail: HelpMessage :: Response
+ Imm.Hooks.SendMail: InsufficientSystemStorage :: Response
+ Imm.Hooks.SendMail: LOGIN :: AuthType
+ Imm.Hooks.SendMail: MAIL :: String -> Command
+ Imm.Hooks.SendMail: Mail :: Address -> [Address] -> [Address] -> [Address] -> Headers -> [Alternatives] -> Mail
+ Imm.Hooks.SendMail: MailboxNotAllowed :: Response
+ Imm.Hooks.SendMail: MailboxUnavailable :: Response
+ Imm.Hooks.SendMail: MailboxUnavailableError :: Response
+ Imm.Hooks.SendMail: NOOP :: Command
+ Imm.Hooks.SendMail: None :: Encoding
+ Imm.Hooks.SendMail: Ok :: Response
+ Imm.Hooks.SendMail: PLAIN :: AuthType
+ Imm.Hooks.SendMail: ParameterError :: Response
+ Imm.Hooks.SendMail: ParameterNotImplemented :: Response
+ Imm.Hooks.SendMail: Part :: Text -> Encoding -> Maybe Text -> Headers -> ByteString -> Part
+ Imm.Hooks.SendMail: QUIT :: Command
+ Imm.Hooks.SendMail: QuotedPrintableBinary :: Encoding
+ Imm.Hooks.SendMail: QuotedPrintableText :: Encoding
+ Imm.Hooks.SendMail: RCPT :: String -> Command
+ Imm.Hooks.SendMail: RSET :: Command
+ Imm.Hooks.SendMail: ServiceClosing :: Response
+ Imm.Hooks.SendMail: ServiceNotAvailable :: Response
+ Imm.Hooks.SendMail: ServiceReady :: Response
+ Imm.Hooks.SendMail: Settings :: PortNumber -> Int -> Bool -> Bool -> Settings
+ Imm.Hooks.SendMail: StartMailInput :: Response
+ Imm.Hooks.SendMail: SyntaxError :: Response
+ Imm.Hooks.SendMail: SystemStatus :: Response
+ Imm.Hooks.SendMail: TransactionFailed :: Response
+ Imm.Hooks.SendMail: UserNotLocal :: Response
+ Imm.Hooks.SendMail: UserNotLocalError :: Response
+ Imm.Hooks.SendMail: VRFY :: String -> Command
+ Imm.Hooks.SendMail: [addressEmail] :: Address -> Text
+ Imm.Hooks.SendMail: [addressName] :: Address -> Maybe Text
+ Imm.Hooks.SendMail: [mailBcc] :: Mail -> [Address]
+ Imm.Hooks.SendMail: [mailCc] :: Mail -> [Address]
+ Imm.Hooks.SendMail: [mailFrom] :: Mail -> Address
+ Imm.Hooks.SendMail: [mailHeaders] :: Mail -> Headers
+ Imm.Hooks.SendMail: [mailParts] :: Mail -> [Alternatives]
+ Imm.Hooks.SendMail: [mailTo] :: Mail -> [Address]
+ Imm.Hooks.SendMail: [partContent] :: Part -> ByteString
+ Imm.Hooks.SendMail: [partEncoding] :: Part -> Encoding
+ Imm.Hooks.SendMail: [partFilename] :: Part -> Maybe Text
+ Imm.Hooks.SendMail: [partHeaders] :: Part -> Headers
+ Imm.Hooks.SendMail: [partType] :: Part -> Text
+ Imm.Hooks.SendMail: [sslDisableCertificateValidation] :: Settings -> Bool
+ Imm.Hooks.SendMail: [sslLogToConsole] :: Settings -> Bool
+ Imm.Hooks.SendMail: [sslMaxLineLength] :: Settings -> Int
+ Imm.Hooks.SendMail: [sslPort] :: Settings -> PortNumber
+ Imm.Hooks.SendMail: [unBoundary] :: Boundary -> Text
+ Imm.Hooks.SendMail: addAttachment :: Text -> FilePath -> Mail -> IO Mail
+ Imm.Hooks.SendMail: addAttachmentBS :: Text -> Text -> ByteString -> Mail -> Mail
+ Imm.Hooks.SendMail: addAttachmentBSCid :: Text -> Text -> ByteString -> Text -> Mail -> Mail
+ Imm.Hooks.SendMail: addAttachmentCid :: Text -> FilePath -> Text -> Mail -> IO Mail
+ Imm.Hooks.SendMail: addAttachments :: [(Text, FilePath)] -> Mail -> IO Mail
+ Imm.Hooks.SendMail: addAttachmentsBS :: [(Text, Text, ByteString)] -> Mail -> Mail
+ Imm.Hooks.SendMail: addPart :: Alternatives -> Mail -> Mail
+ Imm.Hooks.SendMail: authenticate :: AuthType -> UserName -> Password -> SMTPConnection -> IO Bool
+ Imm.Hooks.SendMail: closeSMTP :: SMTPConnection -> IO ()
+ Imm.Hooks.SendMail: connectSMTP :: String -> IO SMTPConnection
+ Imm.Hooks.SendMail: connectSMTPPort :: String -> PortNumber -> IO SMTPConnection
+ Imm.Hooks.SendMail: connectSMTPSSL :: String -> IO SMTPConnection
+ Imm.Hooks.SendMail: connectSMTPSSLWithSettings :: String -> Settings -> IO SMTPConnection
+ Imm.Hooks.SendMail: connectSMTPSTARTTLS :: String -> IO SMTPConnection
+ Imm.Hooks.SendMail: connectSMTPSTARTTLSWithSettings :: String -> Settings -> IO SMTPConnection
+ Imm.Hooks.SendMail: connectStream :: BSStream -> IO SMTPConnection
+ Imm.Hooks.SendMail: data Address
+ Imm.Hooks.SendMail: data AuthType
+ Imm.Hooks.SendMail: data Command
+ Imm.Hooks.SendMail: data Encoding
+ Imm.Hooks.SendMail: data Mail
+ Imm.Hooks.SendMail: data Part
+ Imm.Hooks.SendMail: data Response
+ Imm.Hooks.SendMail: data SMTPConnection
+ Imm.Hooks.SendMail: data Settings
+ Imm.Hooks.SendMail: defaultSettingsSMTPSSL :: Settings
+ Imm.Hooks.SendMail: defaultSettingsSMTPSTARTTLS :: Settings
+ Imm.Hooks.SendMail: doSMTP :: () => String -> (SMTPConnection -> IO a) -> IO a
+ Imm.Hooks.SendMail: doSMTPPort :: () => String -> PortNumber -> (SMTPConnection -> IO a) -> IO a
+ Imm.Hooks.SendMail: doSMTPSSL :: () => String -> (SMTPConnection -> IO a) -> IO a
+ Imm.Hooks.SendMail: doSMTPSSLWithSettings :: () => String -> Settings -> (SMTPConnection -> IO a) -> IO a
+ Imm.Hooks.SendMail: doSMTPSTARTTLS :: () => String -> (SMTPConnection -> IO a) -> IO a
+ Imm.Hooks.SendMail: doSMTPSTARTTLSWithSettings :: () => String -> Settings -> (SMTPConnection -> IO a) -> IO a
+ Imm.Hooks.SendMail: doSMTPStream :: () => BSStream -> (SMTPConnection -> IO a) -> IO a
+ Imm.Hooks.SendMail: emptyMail :: Address -> Mail
+ Imm.Hooks.SendMail: htmlPart :: Text -> Part
+ Imm.Hooks.SendMail: mkHandle :: MonadBase IO m => SendMailSettings -> Handle m
+ Imm.Hooks.SendMail: newtype Boundary
+ Imm.Hooks.SendMail: plainPart :: Text -> Part
+ Imm.Hooks.SendMail: quotedPrintable :: Bool -> ByteString -> Builder
+ Imm.Hooks.SendMail: randomString :: RandomGen d => Int -> d -> (String, d)
+ Imm.Hooks.SendMail: renderAddress :: Address -> Text
+ Imm.Hooks.SendMail: renderMail :: RandomGen g => g -> Mail -> (ByteString, g)
+ Imm.Hooks.SendMail: renderMail' :: Mail -> IO ByteString
+ Imm.Hooks.SendMail: renderSendMail :: Mail -> IO ()
+ Imm.Hooks.SendMail: renderSendMailCustom :: FilePath -> [String] -> Mail -> IO ()
+ Imm.Hooks.SendMail: sendCommand :: SMTPConnection -> Command -> IO (ReplyCode, ByteString)
+ Imm.Hooks.SendMail: sendMail :: String -> [String] -> ByteString -> SMTPConnection -> IO ()
+ Imm.Hooks.SendMail: sendMimeMail :: String -> String -> String -> Text -> Text -> [(Text, FilePath)] -> SMTPConnection -> IO ()
+ Imm.Hooks.SendMail: sendMimeMail' :: String -> String -> String -> Text -> Text -> [(Text, Text, ByteString)] -> SMTPConnection -> IO ()
+ Imm.Hooks.SendMail: sendMimeMail2 :: Mail -> SMTPConnection -> IO ()
+ Imm.Hooks.SendMail: sendPlainTextMail :: String -> String -> String -> Text -> SMTPConnection -> IO ()
+ Imm.Hooks.SendMail: sendmailCustom :: FilePath -> [String] -> ByteString -> IO ()
+ Imm.Hooks.SendMail: sendmailCustomCaptureOutput :: FilePath -> [String] -> ByteString -> IO (ByteString, ByteString)
+ Imm.Hooks.SendMail: simpleMail :: Address -> Address -> Text -> Text -> Text -> [(Text, FilePath)] -> IO Mail
+ Imm.Hooks.SendMail: simpleMail' :: Address -> Address -> Text -> Text -> Mail
+ Imm.Hooks.SendMail: simpleMailInMemory :: Address -> Address -> Text -> Text -> Text -> [(Text, Text, ByteString)] -> Mail
+ Imm.Hooks.SendMail: type Alternatives = [Part]
+ Imm.Hooks.SendMail: type Headers = [(ByteString, Text)]
+ Imm.Hooks.WriteFile: mkHandle :: MonadBase IO m => MonadIO m => MonadMask m => WriteFileSettings -> Handle m
+ Imm.Logger: Handle :: (LogLevel -> Doc AnsiStyle -> m ()) -> m LogLevel -> (LogLevel -> m ()) -> (Bool -> m ()) -> m () -> Handle m
+ Imm.Logger: [flushLogs] :: Handle m -> m ()
+ Imm.Logger: [getLogLevel] :: Handle m -> m LogLevel
+ Imm.Logger: [log] :: Handle m -> LogLevel -> Doc AnsiStyle -> m ()
+ Imm.Logger: [setColorizeLogs] :: Handle m -> Bool -> m ()
+ Imm.Logger: [setLogLevel] :: Handle m -> LogLevel -> m ()
+ Imm.Logger: data Handle m
+ Imm.Logger.Simple: mkHandle :: MonadBaseControl IO m => MVar LoggerSettings -> Handle m
+ Imm.Prelude: (!!) :: () => [a] -> Int -> a
+ Imm.Prelude: ($!) :: () => (a -> b) -> a -> b
+ Imm.Prelude: ($) :: () => (a -> b) -> a -> b
+ Imm.Prelude: (&&) :: Bool -> Bool -> Bool
+ Imm.Prelude: (*) :: Num a => a -> a -> a
+ Imm.Prelude: (**) :: Floating a => a -> a -> a
+ Imm.Prelude: (*>) :: Applicative f => f a -> f b -> f b
+ Imm.Prelude: (+) :: Num a => a -> a -> a
+ Imm.Prelude: (++) :: () => [a] -> [a] -> [a]
+ Imm.Prelude: (-) :: Num a => a -> a -> a
+ Imm.Prelude: (.) :: () => (b -> c) -> (a -> b) -> a -> c
+ Imm.Prelude: (/) :: Fractional a => a -> a -> a
+ Imm.Prelude: (/=) :: Eq a => a -> a -> Bool
+ Imm.Prelude: (<$) :: Functor f => a -> f b -> f a
+ Imm.Prelude: (<$>) :: Functor f => (a -> b) -> f a -> f b
+ Imm.Prelude: (<) :: Ord a => a -> a -> Bool
+ Imm.Prelude: (<*) :: Applicative f => f a -> f b -> f a
+ Imm.Prelude: (<**>) :: Applicative f => f a -> f (a -> b) -> f b
+ Imm.Prelude: (<*>) :: Applicative f => f (a -> b) -> f a -> f b
+ Imm.Prelude: (<=) :: Ord a => a -> a -> Bool
+ Imm.Prelude: (<>) :: Semigroup a => a -> a -> a
+ Imm.Prelude: (<|>) :: Alternative f => f a -> f a -> f a
+ Imm.Prelude: (=<<) :: Monad m => (a -> m b) -> m a -> m b
+ Imm.Prelude: (==) :: Eq a => a -> a -> Bool
+ Imm.Prelude: (>) :: Ord a => a -> a -> Bool
+ Imm.Prelude: (>=) :: Ord a => a -> a -> Bool
+ Imm.Prelude: (>>) :: Monad m => m a -> m b -> m b
+ Imm.Prelude: (>>=) :: Monad m => m a -> (a -> m b) -> m b
+ Imm.Prelude: (^) :: (Num a, Integral b) => a -> b -> a
+ Imm.Prelude: (^^) :: (Fractional a, Integral b) => a -> b -> a
+ Imm.Prelude: (||) :: Bool -> Bool -> Bool
+ Imm.Prelude: -- e.g., the <a>IsMap</a> instance of associated lists.
+ Imm.Prelude: -- | Type of the key set.
+ Imm.Prelude: AppendMode :: IOMode
+ Imm.Prelude: Const :: a -> Const a
+ Imm.Prelude: Down :: a -> Down a
+ Imm.Prelude: EQ :: Ordering
+ Imm.Prelude: False :: Bool
+ Imm.Prelude: GT :: Ordering
+ Imm.Prelude: Just :: a -> Maybe a
+ Imm.Prelude: LT :: Ordering
+ Imm.Prelude: Left :: a -> Either a b
+ Imm.Prelude: Nothing :: Maybe a
+ Imm.Prelude: ReadMode :: IOMode
+ Imm.Prelude: ReadWriteMode :: IOMode
+ Imm.Prelude: Right :: b -> Either a b
+ Imm.Prelude: StringException :: String -> CallStack -> StringException
+ Imm.Prelude: True :: Bool
+ Imm.Prelude: WrapArrow :: a b c -> WrappedArrow b c
+ Imm.Prelude: WrapMonad :: m a -> WrappedMonad a
+ Imm.Prelude: WriteMode :: IOMode
+ Imm.Prelude: ZipList :: [a] -> ZipList a
+ Imm.Prelude: [AsyncExceptionWrapper] :: forall e. Exception e => e -> AsyncExceptionWrapper
+ Imm.Prelude: [Handler] :: forall (m :: Type -> Type) a e. Exception e => (e -> m a) -> Handler m a
+ Imm.Prelude: [SomeAsyncException] :: forall e. Exception e => e -> SomeAsyncException
+ Imm.Prelude: [SomeException] :: forall e. Exception e => e -> SomeException
+ Imm.Prelude: [SyncExceptionWrapper] :: forall e. Exception e => e -> SyncExceptionWrapper
+ Imm.Prelude: [getConst] :: Const a -> a
+ Imm.Prelude: [getZipList] :: ZipList a -> [a]
+ Imm.Prelude: [unwrapArrow] :: WrappedArrow b c -> a b c
+ Imm.Prelude: [unwrapMonad] :: WrappedMonad a -> m a
+ Imm.Prelude: abs :: Num a => a -> a
+ Imm.Prelude: acos :: Floating a => a -> a
+ Imm.Prelude: acosh :: Floating a => a -> a
+ Imm.Prelude: adjustMap :: IsMap map => (MapValue map -> MapValue map) -> ContainerKey map -> map -> map
+ Imm.Prelude: adjustWithKey :: IsMap map => (ContainerKey map -> MapValue map -> MapValue map) -> ContainerKey map -> map -> map
+ Imm.Prelude: all :: MonoFoldable mono => (Element mono -> Bool) -> mono -> Bool
+ Imm.Prelude: alterMap :: IsMap map => (Maybe (MapValue map) -> Maybe (MapValue map)) -> ContainerKey map -> map -> map
+ Imm.Prelude: and :: (MonoFoldable mono, Element mono ~ Bool) => mono -> Bool
+ Imm.Prelude: any :: MonoFoldable mono => (Element mono -> Bool) -> mono -> Bool
+ Imm.Prelude: appendFile :: FilePath -> String -> IO ()
+ Imm.Prelude: asTypeOf :: () => a -> a -> a
+ Imm.Prelude: asin :: Floating a => a -> a
+ Imm.Prelude: asinh :: Floating a => a -> a
+ Imm.Prelude: assert :: () => Bool -> a -> a
+ Imm.Prelude: atan :: Floating a => a -> a
+ Imm.Prelude: atan2 :: RealFloat a => a -> a -> a
+ Imm.Prelude: atanh :: Floating a => a -> a
+ Imm.Prelude: bimap :: Bifunctor p => (a -> b) -> (c -> d) -> p a c -> p b d
+ Imm.Prelude: bracket :: MonadMask m => m a -> (a -> m b) -> (a -> m c) -> m c
+ Imm.Prelude: bracketOnError :: MonadMask m => m a -> (a -> m b) -> (a -> m c) -> m c
+ Imm.Prelude: bracketOnError_ :: MonadMask m => m a -> m b -> m c -> m c
+ Imm.Prelude: bracketWithError :: MonadMask m => m a -> (Maybe SomeException -> a -> m b) -> (a -> m c) -> m c
+ Imm.Prelude: bracket_ :: MonadMask m => m a -> m b -> m c -> m c
+ Imm.Prelude: break :: IsSequence seq => (Element seq -> Bool) -> seq -> (seq, seq)
+ Imm.Prelude: breakLine :: Textual t => t -> (t, t)
+ Imm.Prelude: breakWord :: Textual t => t -> (t, t)
+ Imm.Prelude: catMaybes :: (IsSequence (f (Maybe t)), Functor f, Element (f (Maybe t)) ~ Maybe t) => f (Maybe t) -> f t
+ Imm.Prelude: catch :: (MonadCatch m, Exception e) => m a -> (e -> m a) -> m a
+ Imm.Prelude: catchAny :: MonadCatch m => m a -> (SomeException -> m a) -> m a
+ Imm.Prelude: catchAnyDeep :: (MonadCatch m, MonadIO m, NFData a) => m a -> (SomeException -> m a) -> m a
+ Imm.Prelude: catchAsync :: (MonadCatch m, Exception e) => m a -> (e -> m a) -> m a
+ Imm.Prelude: catchDeep :: (MonadCatch m, MonadIO m, Exception e, NFData a) => m a -> (e -> m a) -> m a
+ Imm.Prelude: catchIO :: MonadCatch m => m a -> (IOException -> m a) -> m a
+ Imm.Prelude: catchIOError :: MonadCatch m => m a -> (IOError -> m a) -> m a
+ Imm.Prelude: catchJust :: (MonadCatch m, Exception e) => (e -> Maybe b) -> m a -> (b -> m a) -> m a
+ Imm.Prelude: catches :: (MonadCatch m, MonadThrow m) => m a -> [Handler m a] -> m a
+ Imm.Prelude: catchesAsync :: (MonadCatch m, MonadThrow m) => m a -> [Handler m a] -> m a
+ Imm.Prelude: catchesDeep :: (MonadCatch m, MonadThrow m, MonadIO m, NFData a) => m a -> [Handler m a] -> m a
+ Imm.Prelude: ceiling :: (RealFrac a, Integral b) => a -> b
+ Imm.Prelude: class Applicative f => Alternative (f :: Type -> Type)
+ Imm.Prelude: class Functor f => Applicative (f :: Type -> Type)
+ Imm.Prelude: class BiPolyMap (map :: Type -> Type -> Type) where {
+ Imm.Prelude: class Bifunctor (p :: Type -> Type -> Type)
+ Imm.Prelude: class Bounded a
+ Imm.Prelude: class Enum a
+ Imm.Prelude: class Eq a
+ Imm.Prelude: class (Typeable e, Show e) => Exception e
+ Imm.Prelude: class Fractional a => Floating a
+ Imm.Prelude: class Foldable (t :: Type -> Type)
+ Imm.Prelude: class Num a => Fractional a
+ Imm.Prelude: class Functor (f :: Type -> Type)
+ Imm.Prelude: class SetContainer set => HasKeysSet set where {
+ Imm.Prelude: class (Real a, Enum a) => Integral a
+ Imm.Prelude: class (MonoTraversable map, SetContainer map) => IsMap map where {
+ Imm.Prelude: class (Monoid seq, MonoTraversable seq, SemiSequence seq, MonoPointed seq) => IsSequence seq
+ Imm.Prelude: class (SetContainer set, Element set ~ ContainerKey set) => IsSet set
+ Imm.Prelude: class IsString a
+ Imm.Prelude: class (IsSequence lazy, IsSequence strict) => LazySequence lazy strict | lazy -> strict, strict -> lazy
+ Imm.Prelude: class Applicative m => Monad (m :: Type -> Type)
+ Imm.Prelude: class (Applicative b, Applicative m, Monad b, Monad m) => MonadBase (b :: Type -> Type) (m :: Type -> Type) | m -> b
+ Imm.Prelude: class MonadThrow m => MonadCatch (m :: Type -> Type)
+ Imm.Prelude: class Monad m => MonadIO (m :: Type -> Type)
+ Imm.Prelude: class MonadCatch m => MonadMask (m :: Type -> Type)
+ Imm.Prelude: class (Alternative m, Monad m) => MonadPlus (m :: Type -> Type)
+ Imm.Prelude: class Monad m => MonadThrow (m :: Type -> Type)
+ Imm.Prelude: class MonoFunctor mono => MonoZip mono
+ Imm.Prelude: class Semigroup a => Monoid a
+ Imm.Prelude: class Num a
+ Imm.Prelude: class Eq a => Ord a
+ Imm.Prelude: class PolyMap (map :: Type -> Type)
+ Imm.Prelude: class Read a
+ Imm.Prelude: class (Num a, Ord a) => Real a
+ Imm.Prelude: class (RealFrac a, Floating a) => RealFloat a
+ Imm.Prelude: class (Real a, Fractional a) => RealFrac a
+ Imm.Prelude: class (Integral Index seq, GrowingAppend seq) => SemiSequence seq where {
+ Imm.Prelude: class Semigroup a
+ Imm.Prelude: class (Monoid set, Semigroup set, MonoFoldable set, Eq ContainerKey set, GrowingAppend set) => SetContainer set where {
+ Imm.Prelude: class Show a
+ Imm.Prelude: class (IsSequence t, IsString t, Element t ~ Char) => Textual t
+ Imm.Prelude: class (IsString t, LeftReductiveMonoid t, LeftGCDMonoid t, FactorialMonoid t) => TextualMonoid t
+ Imm.Prelude: class (Functor t, Foldable t) => Traversable (t :: Type -> Type)
+ Imm.Prelude: class Typeable (a :: k)
+ Imm.Prelude: class (Textual textual, IsSequence binary) => Utf8 textual binary | textual -> binary, binary -> textual
+ Imm.Prelude: compare :: Ord a => a -> a -> Ordering
+ Imm.Prelude: compareLength :: (MonoFoldable mono, Integral i) => mono -> i -> Ordering
+ Imm.Prelude: comparing :: Ord a => (b -> a) -> b -> b -> Ordering
+ Imm.Prelude: concat :: (MonoFoldable mono, Monoid (Element mono)) => mono -> Element mono
+ Imm.Prelude: concatMap :: (MonoFoldable mono, Monoid m) => (Element mono -> m) -> mono -> m
+ Imm.Prelude: cons :: SemiSequence seq => Element seq -> seq -> seq
+ Imm.Prelude: const :: () => a -> b -> a
+ Imm.Prelude: cos :: Floating a => a -> a
+ Imm.Prelude: cosh :: Floating a => a -> a
+ Imm.Prelude: curry :: () => ((a, b) -> c) -> a -> b -> c
+ Imm.Prelude: cycle :: () => [a] -> [a]
+ Imm.Prelude: data AsyncExceptionWrapper
+ Imm.Prelude: data Bool
+ Imm.Prelude: data Char
+ Imm.Prelude: data Double
+ Imm.Prelude: data Either a b
+ Imm.Prelude: data Float
+ Imm.Prelude: data Handler (m :: Type -> Type) a
+ Imm.Prelude: data IO a
+ Imm.Prelude: data IOException
+ Imm.Prelude: data IOMode
+ Imm.Prelude: data Int
+ Imm.Prelude: data Integer
+ Imm.Prelude: data Maybe a
+ Imm.Prelude: data Ordering
+ Imm.Prelude: data SomeAsyncException
+ Imm.Prelude: data SomeException
+ Imm.Prelude: data StringException
+ Imm.Prelude: data SyncExceptionWrapper
+ Imm.Prelude: data Word
+ Imm.Prelude: decodeFloat :: RealFloat a => a -> (Integer, Int)
+ Imm.Prelude: decodeUtf8 :: Utf8 textual binary => binary -> textual
+ Imm.Prelude: defaultCons :: IsSequence seq => Element seq -> seq -> seq
+ Imm.Prelude: defaultFind :: MonoFoldable seq => (Element seq -> Bool) -> seq -> Maybe (Element seq)
+ Imm.Prelude: defaultIntersperse :: IsSequence seq => Element seq -> seq -> seq
+ Imm.Prelude: defaultReverse :: IsSequence seq => seq -> seq
+ Imm.Prelude: defaultSnoc :: IsSequence seq => seq -> Element seq -> seq
+ Imm.Prelude: defaultSortBy :: IsSequence seq => (Element seq -> Element seq -> Ordering) -> seq -> seq
+ Imm.Prelude: defaultSplitWhen :: IsSequence seq => (Element seq -> Bool) -> seq -> [seq]
+ Imm.Prelude: delete :: (IsSequence seq, Eq (Element seq)) => Element seq -> seq -> seq
+ Imm.Prelude: deleteBy :: (IsSequence seq, Eq (Element seq)) => (Element seq -> Element seq -> Bool) -> Element seq -> seq -> seq
+ Imm.Prelude: deleteMap :: IsMap map => ContainerKey map -> map -> map
+ Imm.Prelude: deleteSet :: IsSet set => Element set -> set -> set
+ Imm.Prelude: difference :: SetContainer set => set -> set -> set
+ Imm.Prelude: differenceMap :: PolyMap map => map value1 -> map value2 -> map value1
+ Imm.Prelude: displayException :: Exception e => e -> String
+ Imm.Prelude: div :: Integral a => a -> a -> a
+ Imm.Prelude: divMod :: Integral a => a -> a -> (a, a)
+ Imm.Prelude: drop :: IsSequence seq => Index seq -> seq -> seq
+ Imm.Prelude: dropEnd :: IsSequence seq => Index seq -> seq -> seq
+ Imm.Prelude: dropPrefix :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> seq
+ Imm.Prelude: dropSuffix :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> seq
+ Imm.Prelude: dropWhile :: IsSequence seq => (Element seq -> Bool) -> seq -> seq
+ Imm.Prelude: either :: () => (a -> c) -> (b -> c) -> Either a b -> c
+ Imm.Prelude: elem :: (MonoFoldable mono, Eq (Element mono)) => Element mono -> mono -> Bool
+ Imm.Prelude: empty :: Alternative f => f a
+ Imm.Prelude: encodeFloat :: RealFloat a => Integer -> Int -> a
+ Imm.Prelude: encodeUtf8 :: Utf8 textual binary => textual -> binary
+ Imm.Prelude: ensurePrefix :: (Eq (Element seq), IsSequence seq) => seq -> seq -> seq
+ Imm.Prelude: ensureSuffix :: (Eq (Element seq), IsSequence seq) => seq -> seq -> seq
+ Imm.Prelude: enumFrom :: Enum a => a -> [a]
+ Imm.Prelude: enumFromThen :: Enum a => a -> a -> [a]
+ Imm.Prelude: enumFromThenTo :: Enum a => a -> a -> a -> [a]
+ Imm.Prelude: enumFromTo :: Enum a => a -> a -> [a]
+ Imm.Prelude: error :: HasCallStack => [Char] -> a
+ Imm.Prelude: errorWithoutStackTrace :: () => [Char] -> a
+ Imm.Prelude: even :: Integral a => a -> Bool
+ Imm.Prelude: exp :: Floating a => a -> a
+ Imm.Prelude: exponent :: RealFloat a => a -> Int
+ Imm.Prelude: fail :: Monad m => String -> m a
+ Imm.Prelude: filter :: IsSequence seq => (Element seq -> Bool) -> seq -> seq
+ Imm.Prelude: filterM :: (IsSequence seq, Monad m) => (Element seq -> m Bool) -> seq -> m seq
+ Imm.Prelude: filterMap :: IsMap map => (MapValue map -> Bool) -> map -> map
+ Imm.Prelude: finally :: MonadMask m => m a -> m b -> m a
+ Imm.Prelude: find :: SemiSequence seq => (Element seq -> Bool) -> seq -> Maybe (Element seq)
+ Imm.Prelude: findWithDefault :: IsMap map => MapValue map -> ContainerKey map -> map -> MapValue map
+ Imm.Prelude: first :: Bifunctor p => (a -> b) -> p a c -> p b c
+ Imm.Prelude: flip :: () => (a -> b -> c) -> b -> a -> c
+ Imm.Prelude: floatDigits :: RealFloat a => a -> Int
+ Imm.Prelude: floatRadix :: RealFloat a => a -> Integer
+ Imm.Prelude: floatRange :: RealFloat a => a -> (Int, Int)
+ Imm.Prelude: floor :: (RealFrac a, Integral b) => a -> b
+ Imm.Prelude: fmap :: Functor f => (a -> b) -> f a -> f b
+ Imm.Prelude: fold :: (MonoFoldable mono, Monoid (Element mono)) => mono -> Element mono
+ Imm.Prelude: foldM :: (MonoFoldable mono, Monad m) => (a -> Element mono -> m a) -> a -> mono -> m a
+ Imm.Prelude: foldMap :: (MonoFoldable mono, Monoid m) => (Element mono -> m) -> mono -> m
+ Imm.Prelude: foldMap1Ex :: (MonoFoldable mono, Semigroup m) => (Element mono -> m) -> mono -> m
+ Imm.Prelude: foldl :: Foldable t => (b -> a -> b) -> b -> t a -> b
+ Imm.Prelude: foldl' :: MonoFoldable mono => (a -> Element mono -> a) -> a -> mono -> a
+ Imm.Prelude: foldl1 :: Foldable t => (a -> a -> a) -> t a -> a
+ Imm.Prelude: foldl1Ex' :: MonoFoldable mono => (Element mono -> Element mono -> Element mono) -> mono -> Element mono
+ Imm.Prelude: foldlM :: (MonoFoldable mono, Monad m) => (a -> Element mono -> m a) -> a -> mono -> m a
+ Imm.Prelude: foldr :: MonoFoldable mono => (Element mono -> b -> b) -> b -> mono -> b
+ Imm.Prelude: foldr1 :: Foldable t => (a -> a -> a) -> t a -> a
+ Imm.Prelude: foldr1Ex :: MonoFoldable mono => (Element mono -> Element mono -> Element mono) -> mono -> Element mono
+ Imm.Prelude: for :: (Traversable t, Applicative f) => t a -> (a -> f b) -> f (t b)
+ Imm.Prelude: forM :: (Traversable t, Monad m) => t a -> (a -> m b) -> m (t b)
+ Imm.Prelude: forM_ :: (Foldable t, Monad m) => t a -> (a -> m b) -> m ()
+ Imm.Prelude: for_ :: (MonoFoldable mono, Applicative f) => mono -> (Element mono -> f b) -> f ()
+ Imm.Prelude: fromChunks :: LazySequence lazy strict => [strict] -> lazy
+ Imm.Prelude: fromEnum :: Enum a => a -> Int
+ Imm.Prelude: fromException :: Exception e => SomeException -> Maybe e
+ Imm.Prelude: fromInteger :: Num a => Integer -> a
+ Imm.Prelude: fromIntegral :: (Integral a, Num b) => a -> b
+ Imm.Prelude: fromJust :: () => Maybe a -> a
+ Imm.Prelude: fromLeft :: () => a -> Either a b -> a
+ Imm.Prelude: fromList :: IsSequence seq => [Element seq] -> seq
+ Imm.Prelude: fromMaybe :: () => a -> Maybe a -> a
+ Imm.Prelude: fromRational :: Fractional a => Rational -> a
+ Imm.Prelude: fromRight :: () => b -> Either a b -> b
+ Imm.Prelude: fromStrict :: LazySequence lazy strict => strict -> lazy
+ Imm.Prelude: fromString :: IsString a => String -> a
+ Imm.Prelude: fromText :: TextualMonoid t => Text -> t
+ Imm.Prelude: fst :: () => (a, b) -> a
+ Imm.Prelude: gcd :: Integral a => a -> a -> a
+ Imm.Prelude: generalBracket :: MonadMask m => m a -> (a -> ExitCase b -> m c) -> (a -> m b) -> m (b, c)
+ Imm.Prelude: getChar :: IO Char
+ Imm.Prelude: getContents :: IO String
+ Imm.Prelude: getLine :: IO Text
+ Imm.Prelude: group :: (IsSequence seq, Eq (Element seq)) => seq -> [seq]
+ Imm.Prelude: groupAll :: (IsSequence seq, Eq (Element seq)) => seq -> [seq]
+ Imm.Prelude: groupAllOn :: (IsSequence seq, Eq b) => (Element seq -> b) -> seq -> [seq]
+ Imm.Prelude: groupBy :: IsSequence seq => (Element seq -> Element seq -> Bool) -> seq -> [seq]
+ Imm.Prelude: handleAny :: MonadCatch m => (SomeException -> m a) -> m a -> m a
+ Imm.Prelude: handleAnyDeep :: (MonadCatch m, MonadIO m, NFData a) => (SomeException -> m a) -> m a -> m a
+ Imm.Prelude: handleAsync :: (MonadCatch m, Exception e) => (e -> m a) -> m a -> m a
+ Imm.Prelude: handleDeep :: (MonadCatch m, Exception e, MonadIO m, NFData a) => (e -> m a) -> m a -> m a
+ Imm.Prelude: handleIO :: MonadCatch m => (IOException -> m a) -> m a -> m a
+ Imm.Prelude: handleIOError :: MonadCatch m => (IOError -> m a) -> m a -> m a
+ Imm.Prelude: handleJust :: (MonadCatch m, Exception e) => (e -> Maybe b) -> (b -> m a) -> m a -> m a
+ Imm.Prelude: head :: () => [a] -> a
+ Imm.Prelude: id :: () => a -> a
+ Imm.Prelude: impureThrow :: Exception e => e -> a
+ Imm.Prelude: index :: IsSequence seq => seq -> Index seq -> Maybe (Element seq)
+ Imm.Prelude: indexEx :: IsSequence seq => seq -> Index seq -> Element seq
+ Imm.Prelude: infix 4 <=
+ Imm.Prelude: infixl 1 >>
+ Imm.Prelude: infixl 3 <|>
+ Imm.Prelude: infixl 4 <**>
+ Imm.Prelude: infixl 6 -
+ Imm.Prelude: infixl 7 *
+ Imm.Prelude: infixl 9 !!
+ Imm.Prelude: infixr 0 $!
+ Imm.Prelude: infixr 1 =<<
+ Imm.Prelude: infixr 2 ||
+ Imm.Prelude: infixr 3 &&
+ Imm.Prelude: infixr 5 ++
+ Imm.Prelude: infixr 6 <>
+ Imm.Prelude: infixr 8 ^
+ Imm.Prelude: infixr 9 .
+ Imm.Prelude: init :: () => [a] -> [a]
+ Imm.Prelude: initDef :: IsSequence seq => seq -> seq
+ Imm.Prelude: initEx :: IsSequence seq => seq -> seq
+ Imm.Prelude: initMay :: IsSequence seq => seq -> Maybe seq
+ Imm.Prelude: insertLookupWithKey :: IsMap map => (ContainerKey map -> MapValue map -> MapValue map -> MapValue map) -> ContainerKey map -> MapValue map -> map -> (Maybe (MapValue map), map)
+ Imm.Prelude: insertMap :: IsMap map => ContainerKey map -> MapValue map -> map -> map
+ Imm.Prelude: insertSet :: IsSet set => Element set -> set -> set
+ Imm.Prelude: insertWith :: IsMap map => (MapValue map -> MapValue map -> MapValue map) -> ContainerKey map -> MapValue map -> map -> map
+ Imm.Prelude: insertWithKey :: IsMap map => (ContainerKey map -> MapValue map -> MapValue map -> MapValue map) -> ContainerKey map -> MapValue map -> map -> map
+ Imm.Prelude: interact :: (String -> String) -> IO ()
+ Imm.Prelude: intercalate :: (MonoFoldable mono, Monoid (Element mono)) => Element mono -> mono -> Element mono
+ Imm.Prelude: intersection :: SetContainer set => set -> set -> set
+ Imm.Prelude: intersectionMap :: PolyMap map => map value1 -> map value2 -> map value1
+ Imm.Prelude: intersectionWithMap :: PolyMap map => (value1 -> value2 -> value3) -> map value1 -> map value2 -> map value3
+ Imm.Prelude: intersperse :: SemiSequence seq => Element seq -> seq -> seq
+ Imm.Prelude: ioError :: () => IOError -> IO a
+ Imm.Prelude: isAsyncException :: Exception e => e -> Bool
+ Imm.Prelude: isDenormalized :: RealFloat a => a -> Bool
+ Imm.Prelude: isIEEE :: RealFloat a => a -> Bool
+ Imm.Prelude: isInfinite :: RealFloat a => a -> Bool
+ Imm.Prelude: isInfixOf :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> Bool
+ Imm.Prelude: isJust :: () => Maybe a -> Bool
+ Imm.Prelude: isLeft :: () => Either a b -> Bool
+ Imm.Prelude: isNaN :: RealFloat a => a -> Bool
+ Imm.Prelude: isNegativeZero :: RealFloat a => a -> Bool
+ Imm.Prelude: isNothing :: () => Maybe a -> Bool
+ Imm.Prelude: isPrefixOf :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> Bool
+ Imm.Prelude: isRight :: () => Either a b -> Bool
+ Imm.Prelude: isSuffixOf :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> Bool
+ Imm.Prelude: isSyncException :: Exception e => e -> Bool
+ Imm.Prelude: iterate :: () => (a -> a) -> a -> [a]
+ Imm.Prelude: keys :: SetContainer set => set -> [ContainerKey set]
+ Imm.Prelude: keysSet :: HasKeysSet set => set -> KeySet set
+ Imm.Prelude: last :: () => [a] -> a
+ Imm.Prelude: lcm :: Integral a => a -> a -> a
+ Imm.Prelude: lefts :: () => [Either a b] -> [a]
+ Imm.Prelude: length :: MonoFoldable mono => mono -> Int
+ Imm.Prelude: length64 :: MonoFoldable mono => mono -> Int64
+ Imm.Prelude: lengthIndex :: IsSequence seq => seq -> Index seq
+ Imm.Prelude: lex :: ReadS String
+ Imm.Prelude: lift :: (MonadTrans t, Monad m) => m a -> t m a
+ Imm.Prelude: liftA :: Applicative f => (a -> b) -> f a -> f b
+ Imm.Prelude: liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c
+ Imm.Prelude: liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d
+ Imm.Prelude: liftBase :: MonadBase b m => b α -> m α
+ Imm.Prelude: liftBaseDefault :: (MonadTrans t, MonadBase b m) => b α -> t m α
+ Imm.Prelude: liftIO :: MonadIO m => IO a -> m a
+ Imm.Prelude: lines :: Textual t => t -> [t]
+ Imm.Prelude: listToMaybe :: () => [a] -> Maybe a
+ Imm.Prelude: logBase :: Floating a => a -> a -> a
+ Imm.Prelude: lookup :: IsMap map => ContainerKey map -> map -> Maybe (MapValue map)
+ Imm.Prelude: many :: Alternative f => f a -> f [a]
+ Imm.Prelude: map :: () => (a -> b) -> [a] -> [b]
+ Imm.Prelude: mapFromList :: IsMap map => [(ContainerKey map, MapValue map)] -> map
+ Imm.Prelude: mapKeysWith :: (BiPolyMap map, BPMKeyConstraint map k1, BPMKeyConstraint map k2) => (v -> v -> v) -> (k1 -> k2) -> map k1 v -> map k2 v
+ Imm.Prelude: mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
+ Imm.Prelude: mapM_ :: (Foldable t, Monad m) => (a -> m b) -> t a -> m ()
+ Imm.Prelude: mapMaybe :: () => (a -> Maybe b) -> [a] -> [b]
+ Imm.Prelude: mapToList :: IsMap map => map -> [(ContainerKey map, MapValue map)]
+ Imm.Prelude: mapWithKey :: IsMap map => (ContainerKey map -> MapValue map -> MapValue map) -> map -> map
+ Imm.Prelude: mappend :: Monoid a => a -> a -> a
+ Imm.Prelude: mask :: MonadMask m => ((forall a. () => m a -> m a) -> m b) -> m b
+ Imm.Prelude: mask_ :: MonadMask m => m a -> m a
+ Imm.Prelude: max :: Ord a => a -> a -> a
+ Imm.Prelude: maxBound :: Bounded a => a
+ Imm.Prelude: maximum :: (Foldable t, Ord a) => t a -> a
+ Imm.Prelude: maybe :: () => b -> (a -> b) -> Maybe a -> b
+ Imm.Prelude: maybeToList :: () => Maybe a -> [a]
+ Imm.Prelude: mconcat :: Monoid a => [a] -> a
+ Imm.Prelude: member :: SetContainer set => ContainerKey set -> set -> Bool
+ Imm.Prelude: mempty :: Monoid a => a
+ Imm.Prelude: min :: Ord a => a -> a -> a
+ Imm.Prelude: minBound :: Bounded a => a
+ Imm.Prelude: minimum :: (Foldable t, Ord a) => t a -> a
+ Imm.Prelude: mod :: Integral a => a -> a -> a
+ Imm.Prelude: mplus :: MonadPlus m => m a -> m a -> m a
+ Imm.Prelude: mzero :: MonadPlus m => m a
+ Imm.Prelude: negate :: Num a => a -> a
+ Imm.Prelude: newtype Down a
+ Imm.Prelude: newtype Const a (b :: k) :: forall k. () => Type -> k -> Type
+ Imm.Prelude: newtype WrappedArrow (a :: Type -> Type -> Type) b c
+ Imm.Prelude: newtype WrappedMonad (m :: Type -> Type) a
+ Imm.Prelude: newtype ZipList a
+ Imm.Prelude: not :: Bool -> Bool
+ Imm.Prelude: notElem :: (MonoFoldable mono, Eq (Element mono)) => Element mono -> mono -> Bool
+ Imm.Prelude: notMember :: SetContainer set => ContainerKey set -> set -> Bool
+ Imm.Prelude: null :: MonoFoldable mono => mono -> Bool
+ Imm.Prelude: odd :: Integral a => a -> Bool
+ Imm.Prelude: omapKeysWith :: IsMap map => (MapValue map -> MapValue map -> MapValue map) -> (ContainerKey map -> ContainerKey map) -> map -> map
+ Imm.Prelude: onException :: MonadMask m => m a -> m b -> m a
+ Imm.Prelude: optional :: Alternative f => f a -> f (Maybe a)
+ Imm.Prelude: or :: (MonoFoldable mono, Element mono ~ Bool) => mono -> Bool
+ Imm.Prelude: otherwise :: Bool
+ Imm.Prelude: ounzip :: MonoZip mono => [(Element mono, Element mono)] -> (mono, mono)
+ Imm.Prelude: ozip :: MonoZip mono => mono -> mono -> [(Element mono, Element mono)]
+ Imm.Prelude: ozipWith :: MonoZip mono => (Element mono -> Element mono -> Element mono) -> mono -> mono -> mono
+ Imm.Prelude: pack :: IsSequence seq => [Element seq] -> seq
+ Imm.Prelude: partition :: IsSequence seq => (Element seq -> Bool) -> seq -> (seq, seq)
+ Imm.Prelude: partitionEithers :: () => [Either a b] -> ([a], [b])
+ Imm.Prelude: permutations :: IsSequence seq => seq -> [seq]
+ Imm.Prelude: pi :: Floating a => a
+ Imm.Prelude: point :: MonoPointed mono => Element mono -> mono
+ Imm.Prelude: pred :: Enum a => a -> a
+ Imm.Prelude: print :: Show a => a -> IO ()
+ Imm.Prelude: product :: (MonoFoldable mono, Num (Element mono)) => mono -> Element mono
+ Imm.Prelude: properFraction :: (RealFrac a, Integral b) => a -> (b, a)
+ Imm.Prelude: pure :: Applicative f => a -> f a
+ Imm.Prelude: putChar :: Char -> IO ()
+ Imm.Prelude: putStr :: Text -> IO ()
+ Imm.Prelude: putStrLn :: Text -> IO ()
+ Imm.Prelude: quot :: Integral a => a -> a -> a
+ Imm.Prelude: quotRem :: Integral a => a -> a -> (a, a)
+ Imm.Prelude: read :: Read a => String -> a
+ Imm.Prelude: readIO :: Read a => String -> IO a
+ Imm.Prelude: readList :: Read a => ReadS [a]
+ Imm.Prelude: readLn :: Read a => IO a
+ Imm.Prelude: readParen :: () => Bool -> ReadS a -> ReadS a
+ Imm.Prelude: reads :: Read a => ReadS a
+ Imm.Prelude: readsPrec :: Read a => Int -> ReadS a
+ Imm.Prelude: realToFrac :: (Real a, Fractional b) => a -> b
+ Imm.Prelude: recip :: Fractional a => a -> a
+ Imm.Prelude: rem :: Integral a => a -> a -> a
+ Imm.Prelude: repack :: (MonoFoldable a, IsSequence b, Element a ~ Element b) => a -> b
+ Imm.Prelude: repeat :: () => a -> [a]
+ Imm.Prelude: replaceSeq :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> seq -> seq
+ Imm.Prelude: replaceSeqLazyText :: Text -> Text -> Text -> Text
+ Imm.Prelude: replaceSeqStrictText :: Text -> Text -> Text -> Text
+ Imm.Prelude: replicate :: IsSequence seq => Index seq -> Element seq -> seq
+ Imm.Prelude: replicateM :: (IsSequence seq, Monad m) => Index seq -> m (Element seq) -> m seq
+ Imm.Prelude: return :: Monad m => a -> m a
+ Imm.Prelude: reverse :: SemiSequence seq => seq -> seq
+ Imm.Prelude: rights :: () => [Either a b] -> [b]
+ Imm.Prelude: round :: (RealFrac a, Integral b) => a -> b
+ Imm.Prelude: scaleFloat :: RealFloat a => Int -> a -> a
+ Imm.Prelude: scanl :: () => (b -> a -> b) -> b -> [a] -> [b]
+ Imm.Prelude: scanl1 :: () => (a -> a -> a) -> [a] -> [a]
+ Imm.Prelude: scanr :: () => (a -> b -> b) -> b -> [a] -> [b]
+ Imm.Prelude: scanr1 :: () => (a -> a -> a) -> [a] -> [a]
+ Imm.Prelude: second :: Bifunctor p => (b -> c) -> p a b -> p a c
+ Imm.Prelude: seq :: () => a -> b -> b
+ Imm.Prelude: sequence :: (Traversable t, Monad m) => t (m a) -> m (t a)
+ Imm.Prelude: sequenceA :: (Traversable t, Applicative f) => t (f a) -> f (t a)
+ Imm.Prelude: sequence_ :: (Applicative m, MonoFoldable mono, Element mono ~ m ()) => mono -> m ()
+ Imm.Prelude: setFromList :: IsSet set => [Element set] -> set
+ Imm.Prelude: setToList :: IsSet set => set -> [Element set]
+ Imm.Prelude: showChar :: Char -> ShowS
+ Imm.Prelude: showList :: Show a => [a] -> ShowS
+ Imm.Prelude: showParen :: Bool -> ShowS -> ShowS
+ Imm.Prelude: showString :: String -> ShowS
+ Imm.Prelude: shows :: Show a => a -> ShowS
+ Imm.Prelude: showsPrec :: Show a => Int -> a -> ShowS
+ Imm.Prelude: significand :: RealFloat a => a -> a
+ Imm.Prelude: signum :: Num a => a -> a
+ Imm.Prelude: sin :: Floating a => a -> a
+ Imm.Prelude: singleton :: MonoPointed seq => Element seq -> seq
+ Imm.Prelude: singletonMap :: IsMap map => ContainerKey map -> MapValue map -> map
+ Imm.Prelude: singletonSet :: IsSet set => Element set -> set
+ Imm.Prelude: sinh :: Floating a => a -> a
+ Imm.Prelude: snd :: () => (a, b) -> b
+ Imm.Prelude: snoc :: SemiSequence seq => seq -> Element seq -> seq
+ Imm.Prelude: some :: Alternative f => f a -> f [a]
+ Imm.Prelude: sort :: (SemiSequence seq, Ord (Element seq)) => seq -> seq
+ Imm.Prelude: sortBy :: SemiSequence seq => (Element seq -> Element seq -> Ordering) -> seq -> seq
+ Imm.Prelude: sortOn :: (Ord o, SemiSequence seq) => (Element seq -> o) -> seq -> seq
+ Imm.Prelude: span :: IsSequence seq => (Element seq -> Bool) -> seq -> (seq, seq)
+ Imm.Prelude: splitAt :: IsSequence seq => Index seq -> seq -> (seq, seq)
+ Imm.Prelude: splitElem :: (IsSequence seq, Eq (Element seq)) => Element seq -> seq -> [seq]
+ Imm.Prelude: splitElemStrictBS :: Word8 -> ByteString -> [ByteString]
+ Imm.Prelude: splitSeq :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> [seq]
+ Imm.Prelude: splitSeqLazyBS :: Word8 -> ByteString -> [ByteString]
+ Imm.Prelude: splitSeqLazyText :: Text -> Text -> [Text]
+ Imm.Prelude: splitSeqStrictText :: Text -> Text -> [Text]
+ Imm.Prelude: splitWhen :: IsSequence seq => (Element seq -> Bool) -> seq -> [seq]
+ Imm.Prelude: sqrt :: Floating a => a -> a
+ Imm.Prelude: stderr :: Handle
+ Imm.Prelude: stdout :: Handle
+ Imm.Prelude: stripPrefix :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> Maybe seq
+ Imm.Prelude: stripPrefixLazyBS :: ByteString -> ByteString -> Maybe ByteString
+ Imm.Prelude: stripPrefixStrictBS :: ByteString -> ByteString -> Maybe ByteString
+ Imm.Prelude: stripSuffix :: (IsSequence seq, Eq (Element seq)) => seq -> seq -> Maybe seq
+ Imm.Prelude: stripSuffixLazyBS :: ByteString -> ByteString -> Maybe ByteString
+ Imm.Prelude: stripSuffixStrictBS :: ByteString -> ByteString -> Maybe ByteString
+ Imm.Prelude: subsequences :: IsSequence seq => seq -> [seq]
+ Imm.Prelude: subtract :: Num a => a -> a -> a
+ Imm.Prelude: succ :: Enum a => a -> a
+ Imm.Prelude: sum :: (MonoFoldable mono, Num (Element mono)) => mono -> Element mono
+ Imm.Prelude: tail :: () => [a] -> [a]
+ Imm.Prelude: tailDef :: IsSequence seq => seq -> seq
+ Imm.Prelude: tailEx :: IsSequence seq => seq -> seq
+ Imm.Prelude: tailMay :: IsSequence seq => seq -> Maybe seq
+ Imm.Prelude: take :: IsSequence seq => Index seq -> seq -> seq
+ Imm.Prelude: takeWhile :: IsSequence seq => (Element seq -> Bool) -> seq -> seq
+ Imm.Prelude: tan :: Floating a => a -> a
+ Imm.Prelude: tanh :: Floating a => a -> a
+ Imm.Prelude: throw :: (MonadThrow m, Exception e) => e -> m a
+ Imm.Prelude: throwIO :: (MonadThrow m, Exception e) => e -> m a
+ Imm.Prelude: throwM :: (MonadThrow m, Exception e) => e -> m a
+ Imm.Prelude: throwString :: (MonadThrow m, HasCallStack) => String -> m a
+ Imm.Prelude: throwTo :: (Exception e, MonadIO m) => ThreadId -> e -> m ()
+ Imm.Prelude: toAsyncException :: Exception e => e -> SomeException
+ Imm.Prelude: toCaseFold :: Textual t => t -> t
+ Imm.Prelude: toChunks :: LazySequence lazy strict => lazy -> [strict]
+ Imm.Prelude: toEnum :: Enum a => Int -> a
+ Imm.Prelude: toException :: Exception e => e -> SomeException
+ Imm.Prelude: toInteger :: Integral a => a -> Integer
+ Imm.Prelude: toList :: MonoFoldable mono => mono -> [Element mono]
+ Imm.Prelude: toLower :: Textual t => t -> t
+ Imm.Prelude: toRational :: Real a => a -> Rational
+ Imm.Prelude: toStrict :: LazySequence lazy strict => lazy -> strict
+ Imm.Prelude: toSyncException :: Exception e => e -> SomeException
+ Imm.Prelude: toUpper :: Textual t => t -> t
+ Imm.Prelude: traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
+ Imm.Prelude: traverse_ :: (MonoFoldable mono, Applicative f) => (Element mono -> f b) -> mono -> f ()
+ Imm.Prelude: truncate :: (RealFrac a, Integral b) => a -> b
+ Imm.Prelude: try :: (MonadCatch m, Exception e) => m a -> m (Either e a)
+ Imm.Prelude: tryAny :: MonadCatch m => m a -> m (Either SomeException a)
+ Imm.Prelude: tryAnyDeep :: (MonadCatch m, MonadIO m, NFData a) => m a -> m (Either SomeException a)
+ Imm.Prelude: tryAsync :: (MonadCatch m, Exception e) => m a -> m (Either e a)
+ Imm.Prelude: tryDeep :: (MonadCatch m, MonadIO m, Exception e, NFData a) => m a -> m (Either e a)
+ Imm.Prelude: tryIO :: MonadCatch m => m a -> m (Either IOException a)
+ Imm.Prelude: tryJust :: (MonadCatch m, Exception e) => (e -> Maybe b) -> m a -> m (Either b a)
+ Imm.Prelude: type FilePath = String
+ Imm.Prelude: type IOError = IOException
+ Imm.Prelude: type Rational = Ratio Integer
+ Imm.Prelude: type ReadS a = String -> [(a, String)]
+ Imm.Prelude: type ShowS = String -> String
+ Imm.Prelude: type String = [Char]
+ Imm.Prelude: type family KeySet set :: Type;
+ Imm.Prelude: uncons :: IsSequence seq => seq -> Maybe (Element seq, seq)
+ Imm.Prelude: uncurry :: () => (a -> b -> c) -> (a, b) -> c
+ Imm.Prelude: undefined :: HasCallStack => a
+ Imm.Prelude: uninterruptibleMask :: MonadMask m => ((forall a. () => m a -> m a) -> m b) -> m b
+ Imm.Prelude: uninterruptibleMask_ :: MonadMask m => m a -> m a
+ Imm.Prelude: union :: SetContainer set => set -> set -> set
+ Imm.Prelude: unionWith :: IsMap map => (MapValue map -> MapValue map -> MapValue map) -> map -> map -> map
+ Imm.Prelude: unionWithKey :: IsMap map => (ContainerKey map -> MapValue map -> MapValue map -> MapValue map) -> map -> map -> map
+ Imm.Prelude: unions :: (SetContainer set, MonoFoldable mono, Element mono ~ set) => mono -> set
+ Imm.Prelude: unionsWith :: IsMap map => (MapValue map -> MapValue map -> MapValue map) -> [map] -> map
+ Imm.Prelude: unless :: Applicative f => Bool -> f () -> f ()
+ Imm.Prelude: unlines :: (Textual t, Element seq ~ t, MonoFoldable seq) => seq -> t
+ Imm.Prelude: unpack :: MonoFoldable mono => mono -> [Element mono]
+ Imm.Prelude: unsafeDrop :: IsSequence seq => Index seq -> seq -> seq
+ Imm.Prelude: unsafeIndex :: IsSequence seq => seq -> Index seq -> Element seq
+ Imm.Prelude: unsafeInit :: IsSequence seq => seq -> seq
+ Imm.Prelude: unsafeSplitAt :: IsSequence seq => Index seq -> seq -> (seq, seq)
+ Imm.Prelude: unsafeTail :: IsSequence seq => seq -> seq
+ Imm.Prelude: unsafeTake :: IsSequence seq => Index seq -> seq -> seq
+ Imm.Prelude: unsnoc :: IsSequence seq => seq -> Maybe (seq, Element seq)
+ Imm.Prelude: until :: () => (a -> Bool) -> (a -> a) -> a -> a
+ Imm.Prelude: unwords :: (Textual t, Element seq ~ t, MonoFoldable seq) => seq -> t
+ Imm.Prelude: unzip :: () => [(a, b)] -> ([a], [b])
+ Imm.Prelude: unzip3 :: () => [(a, b, c)] -> ([a], [b], [c])
+ Imm.Prelude: updateLookupWithKey :: IsMap map => (ContainerKey map -> MapValue map -> Maybe (MapValue map)) -> ContainerKey map -> map -> (Maybe (MapValue map), map)
+ Imm.Prelude: updateMap :: IsMap map => (MapValue map -> Maybe (MapValue map)) -> ContainerKey map -> map -> map
+ Imm.Prelude: updateWithKey :: IsMap map => (ContainerKey map -> MapValue map -> Maybe (MapValue map)) -> ContainerKey map -> map -> map
+ Imm.Prelude: userError :: String -> IOError
+ Imm.Prelude: vectorSort :: (Vector v e, Ord e) => v e -> v e
+ Imm.Prelude: vectorSortBy :: Vector v e => (e -> e -> Ordering) -> v e -> v e
+ Imm.Prelude: void :: Functor f => f a -> f ()
+ Imm.Prelude: when :: Applicative f => Bool -> f () -> f ()
+ Imm.Prelude: withException :: (MonadMask m, Exception e) => m a -> (e -> m b) -> m a
+ Imm.Prelude: words :: Textual t => t -> [t]
+ Imm.Prelude: zip :: () => [a] -> [b] -> [(a, b)]
+ Imm.Prelude: zip3 :: () => [a] -> [b] -> [c] -> [(a, b, c)]
+ Imm.Prelude: zipWith :: () => (a -> b -> c) -> [a] -> [b] -> [c]
+ Imm.Prelude: zipWith3 :: () => (a -> b -> c -> d) -> [a] -> [b] -> [c] -> [d]
+ Imm.Prelude: }
+ Imm.XML: Handle :: (URI -> LByteString -> m Feed) -> Handle m
+ Imm.XML: [parseXml] :: Handle m -> URI -> LByteString -> m Feed
+ Imm.XML: newtype Handle m
+ Imm.XML.Conduit: mkHandle :: MonadIO m => MonadCatch m => XmlParser -> Handle m
- Imm.Boot: imm :: ModulesM IO -> IO ()
+ Imm.Boot: imm :: Handle IO -> Handle IO FeedTable -> Handle IO -> Handle IO -> Handle IO -> IO ()
- Imm.Core: check :: (MonadAsync m, MonadCatch m, MonadLog m, MonadDatabase FeedTable m, MonadHttpClient m, MonadXmlParser m) => [FeedID] -> m ()
+ Imm.Core: check :: (MonadAsync m, MonadCatch m) => Handle m -> Handle m FeedTable -> Handle m -> Handle m -> [FeedID] -> m ()
- Imm.Core: importOPML :: (MonadLog m, MonadDatabase FeedTable m, MonadCatch m) => ConduitT () ByteString m () -> m ()
+ Imm.Core: importOPML :: MonadCatch m => Handle m -> Handle m FeedTable -> ConduitT () ByteString m () -> m ()
- Imm.Core: printVersions :: (MonadBase IO m) => m ()
+ Imm.Core: printVersions :: MonadBase IO m => m ()
- Imm.Core: run :: (MonadTime m, MonadAsync m, MonadCatch m, MonadImm m, MonadLog m, MonadDatabase FeedTable m, MonadHttpClient m, MonadXmlParser m) => [FeedID] -> m ()
+ Imm.Core: run :: (MonadTime m, MonadAsync m, MonadCatch m) => Handle m -> Handle m FeedTable -> Handle m -> Handle m -> Handle m -> [FeedID] -> m ()
- Imm.Core: showFeed :: (MonadLog m, MonadThrow m, MonadDatabase FeedTable m) => [FeedID] -> m ()
+ Imm.Core: showFeed :: MonadThrow m => Handle m -> Handle m FeedTable -> [FeedID] -> m ()
- Imm.Core: subscribe :: (MonadLog m, MonadDatabase FeedTable m, MonadCatch m) => URI -> Set Text -> m ()
+ Imm.Core: subscribe :: MonadCatch m => Handle m -> Handle m FeedTable -> URI -> Set Text -> m ()
- Imm.Database: NotUpdated :: t -> (Key t) -> DatabaseException t
+ Imm.Database: NotUpdated :: t -> Key t -> DatabaseException t
- Imm.Database: commit :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> m ()
+ Imm.Database: commit :: Monad m => Handle m -> Handle m t -> m ()
- Imm.Database: delete :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> Key t -> m ()
+ Imm.Database: delete :: Monad m => Handle m -> Handle m t -> Key t -> m ()
- Imm.Database: deleteList :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> [Key t] -> m ()
+ Imm.Database: deleteList :: Monad m => Handle m -> Handle m t -> [Key t] -> m ()
- Imm.Database: fetch :: (MonadDatabase t m, Table t, MonadThrow m) => t -> Key t -> m (Entry t)
+ Imm.Database: fetch :: Monad m => Table t => MonadThrow m => Handle m t -> Key t -> m (Entry t)
- Imm.Database: fetchAll :: (MonadThrow m, MonadDatabase t m) => t -> m (Map (Key t) (Entry t))
+ Imm.Database: fetchAll :: Monad m => Handle m t -> m (Map (Key t) (Entry t))
- Imm.Database: fetchList :: (MonadDatabase t m, MonadThrow m) => t -> [Key t] -> m (Map (Key t) (Entry t))
+ Imm.Database: fetchList :: Monad m => Handle m t -> [Key t] -> m (Map (Key t) (Entry t))
- Imm.Database: insert :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> Key t -> Entry t -> m ()
+ Imm.Database: insert :: Monad m => Handle m -> Handle m t -> Key t -> Entry t -> m ()
- Imm.Database: insertList :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> [(Key t, Entry t)] -> m ()
+ Imm.Database: insertList :: Monad m => Handle m -> Handle m t -> [(Key t, Entry t)] -> m ()
- Imm.Database: purge :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> m ()
+ Imm.Database: purge :: Monad m => Handle m -> Handle m t -> m ()
- Imm.Database: update :: (MonadDatabase t m, MonadThrow m) => t -> Key t -> (Entry t -> Entry t) -> m ()
+ Imm.Database: update :: Monad m => Handle m t -> Key t -> (Entry t -> Entry t) -> m ()
- Imm.Database.FeedTable: addReadHash :: (MonadDatabase FeedTable m, MonadThrow m, MonadLog m) => FeedID -> Int -> m ()
+ Imm.Database.FeedTable: addReadHash :: MonadThrow m => Handle m -> Handle m FeedTable -> FeedID -> Int -> m ()
- Imm.Database.FeedTable: getStatus :: (MonadDatabase FeedTable m, MonadCatch m) => FeedID -> m FeedStatus
+ Imm.Database.FeedTable: getStatus :: MonadCatch m => Handle m FeedTable -> FeedID -> m FeedStatus
- Imm.Database.FeedTable: markAsRead :: (MonadTime m, MonadDatabase FeedTable m, MonadThrow m, MonadLog m) => FeedID -> m ()
+ Imm.Database.FeedTable: markAsRead :: (MonadTime m, MonadThrow m) => Handle m -> Handle m FeedTable -> FeedID -> m ()
- Imm.Database.FeedTable: markAsUnread :: (MonadDatabase FeedTable m, MonadThrow m, MonadLog m) => FeedID -> m ()
+ Imm.Database.FeedTable: markAsUnread :: MonadThrow m => Handle m -> Handle m FeedTable -> FeedID -> m ()
- Imm.Database.FeedTable: register :: (MonadThrow m, MonadLog m, MonadDatabase FeedTable m) => FeedID -> Set Text -> m ()
+ Imm.Database.FeedTable: register :: MonadThrow m => Handle m -> Handle m FeedTable -> FeedID -> Set Text -> m ()
- Imm.Database.JsonFile: mkJsonFileDatabase :: (Table t) => FilePath -> JsonFileDatabase t
+ Imm.Database.JsonFile: mkJsonFileDatabase :: Table t => FilePath -> JsonFileDatabase t
- Imm.Feed: Rss :: (RssDocument '[ContentModule, DublinCoreModule]) -> Feed
+ Imm.Feed: Rss :: RssDocument '[ContentModule, DublinCoreModule] -> Feed
- Imm.Feed: RssElement :: (RssItem '[ContentModule, DublinCoreModule]) -> FeedElement
+ Imm.Feed: RssElement :: RssItem '[ContentModule, DublinCoreModule] -> FeedElement
- Imm.HTTP: get :: (MonadHttpClient m, MonadLog m, MonadThrow m) => URI -> m LByteString
+ Imm.HTTP: get :: Monad m => Handle m -> Handle m -> URI -> m LByteString
- Imm.Hooks: onNewElement :: (MonadImm m, MonadLog m) => Feed -> FeedElement -> m ()
+ Imm.Hooks: onNewElement :: Monad m => Handle m -> Handle m -> Feed -> FeedElement -> m ()
- Imm.Hooks.SendMail: SMTPServer :: (Maybe Authentication) -> ConnectionSettings -> SMTPServer
+ Imm.Hooks.SendMail: SMTPServer :: Maybe Authentication -> ConnectionSettings -> SMTPServer
- Imm.Hooks.WriteFile: convertAtomURI :: (IsString t) => AtomURI -> t
+ Imm.Hooks.WriteFile: convertAtomURI :: IsString t => AtomURI -> t
- Imm.Hooks.WriteFile: convertDoc :: (IsString t) => Doc a -> t
+ Imm.Hooks.WriteFile: convertDoc :: IsString t => Doc a -> t
- Imm.Hooks.WriteFile: convertText :: (IsString t) => Text -> t
+ Imm.Hooks.WriteFile: convertText :: IsString t => Text -> t
- Imm.Hooks.WriteFile: convertURI :: (IsString t) => URIRef a -> t
+ Imm.Hooks.WriteFile: convertURI :: IsString t => URIRef a -> t

Files

README.md view
@@ -9,13 +9,13 @@ To get started, please consult documentation of `Imm.Boot` module.  -## Example workflows+## Example use cases  ### Online feed reader  For the sake of *I-want-the-mutt-of-feed-readers* zealots, it is possible to turn any mail reader into a feed reader, by having *imm* send an e-mail with unread elements to an arbitrary address. You can then browse your feeds through your favourite mail reader, and leverage any mail-related tool on your feeds.-Bonus points if your mail reader is online as you can now access your feeds from anywhere in the internet.+Bonus points if your mail reader is online as you can now access your feeds from any computer connected to the Internet.  Check out `Imm.Hooks.SendMail` module. 
imm.cabal view
@@ -1,5 +1,5 @@ name:                imm-version:             1.3.0.0+version:             1.4.0.0 synopsis:            Execute arbitrary actions for each unread element of RSS/Atom feeds description:         Cf README file homepage:            https://github.com/k0ral/imm@@ -67,6 +67,7 @@     lifted-base,     microlens,     mime-mail,+    monad-control,     monad-time,     monoid-subclasses,     mono-traversable >= 1,@@ -83,7 +84,6 @@     streaming-with,     streamly,     text,-    transformers,     transformers-base,     time,     timerep >= 2.0.0.0,
src/bin/Executable.hs view
@@ -3,21 +3,27 @@  -- {{{ Imports import           Imm-import           Imm.Database.JsonFile-import           Imm.Hooks.Dummy-import           Imm.HTTP.Simple-import           Imm.Logger.Simple+import           Imm.Database.JsonFile as Database+import           Imm.Hooks.Dummy as Hooks+import           Imm.HTTP.Simple as HTTP+import           Imm.Logger.Simple as Logger import           Imm.Prelude-import           Imm.XML.Conduit--import           Control.Concurrent.MVar+import           Imm.XML.Conduit as XML -- }}}   main :: IO () main = do-  logger <- defaultLogger-  manager <- defaultManager-  database <- defaultDatabase :: IO (MVar (JsonFileDatabase FeedTable))+  logger <- Logger.mkHandle <$> defaultLogger+  database <- Database.mkHandle <$> defaultDatabase+  httpClient <- HTTP.mkHandle <$> defaultManager -  imm $ mkModulesM manager database logger DummyHooks defaultXmlParser+  imm logger database httpClient hooks xmlParser++xmlParser :: XML.Handle IO+xmlParser = XML.mkHandle defaultXmlParser++hooks :: Hooks.Handle IO+hooks = Hooks.mkHandle++
src/lib/Imm.hs view
@@ -5,8 +5,8 @@  import           Imm.Boot     as X import           Imm.Core     as X-import           Imm.Database as X+import           Imm.Database as X hiding(Handle) import           Imm.Feed     as X-import           Imm.Hooks    as X-import           Imm.HTTP     as X-import           Imm.Logger   as X+import           Imm.Hooks    as X hiding(Handle)+import           Imm.HTTP     as X hiding(Handle)+import           Imm.Logger   as X hiding(Handle)
src/lib/Imm/Boot.hs view
@@ -16,10 +16,10 @@ -- -- Your personal configuration is located at @$XDG_CONFIG_HOME\/imm\/imm.hs@. ----- == @ReaderT@ pattern+-- == Handle pattern ----- The behavior of this program can be customized through the @ReaderT@ pattern.-module Imm.Boot (imm, Modules(..), ModulesM, mkModulesM) where+-- 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@@ -27,97 +27,44 @@ import           Imm.Database.FeedTable     as Database import           Imm.Dyre                   as Dyre import           Imm.Feed-import           Imm.Hooks+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+import           Imm.XML as XML -import           Control.Monad.Time-import           Control.Monad.Trans.Reader import           Data.Conduit.Combinators   (stdin)-import           Streamly                   (MonadAsync) import           System.IO                  (hFlush) -- }}} --- | Modules are independent features of the program which behavior can be controlled by the user.-data Modules httpClient databaseClient logger hooks xmlParser = Modules-  { _httpClient     :: httpClient      -- ^ HTTP client interpreter (cf "Imm.HTTP")-  , _databaseClient :: databaseClient  -- ^ Database interpreter (cf "Imm.Database")-  , _logger         :: logger          -- ^ Logging interpreter (cf "Imm.Logger")-  , _hooks          :: hooks           -- ^ Hooks interpreter (cf "Imm.Hooks")-  , _xmlParser      :: xmlParser       -- ^ XML parsing interpreter (cf "Imm.XML")-  }---- | Type-erased version of 'Modules', using existential quantification.-data ModulesM m = forall a b c d e .-  ( MonadHttpClient (ReaderT a m)-  , MonadDatabase FeedTable (ReaderT b m)-  , MonadLog (ReaderT c m)-  , MonadImm (ReaderT d m)-  , MonadXmlParser (ReaderT e m)-  ) => ModulesM (Modules a b c d e)---- | Constructor for 'ModulesM'.-mkModulesM :: (MonadXmlParser (ReaderT e m), MonadImm (ReaderT d m), MonadLog (ReaderT c m), MonadDatabase FeedTable (ReaderT b m), MonadHttpClient (ReaderT a m))-           => a -> b -> c -> d -> e -> ModulesM m-mkModulesM a b c d e = ModulesM $ Modules a b c d e---instance (MonadIO m, MonadLog (ReaderT c m)) => MonadLog (ReaderT (Modules a b c d e) m) where-  log l t = withReaderT _logger $ log l t-  getLogLevel = withReaderT _logger getLogLevel-  setLogLevel l = withReaderT _logger $ setLogLevel l-  setColorizeLogs c = withReaderT _logger $ setColorizeLogs c-  flushLogs = withReaderT _logger flushLogs--instance (Monad m, MonadImm (ReaderT d m)) => MonadImm (ReaderT (Modules a b c d e) m) where-  processNewElement feed element = withReaderT _hooks $ processNewElement feed element--instance (MonadThrow m, MonadHttpClient (ReaderT a m)) => MonadHttpClient (ReaderT (Modules a b c d e) m) where-  httpGet uri = withReaderT _httpClient $ httpGet uri--instance (MonadThrow m, MonadXmlParser (ReaderT e m))-  => MonadXmlParser (ReaderT (Modules a b c d e) m) where-  parseXml uri bytes = withReaderT _xmlParser $ parseXml uri bytes--instance (MonadThrow m, MonadDatabase FeedTable (ReaderT b m))-  => MonadDatabase FeedTable (ReaderT (Modules a b c d e) m) where-  _describeDatabase t = withReaderT _databaseClient $ _describeDatabase t-  _fetchList t k = withReaderT _databaseClient $ _fetchList t k-  _fetchAll t = withReaderT _databaseClient $ _fetchAll t-  _update t key f = withReaderT _databaseClient $ _update t key f-  _insertList t list = withReaderT _databaseClient $ _insertList t list-  _deleteList t k = withReaderT _databaseClient $ _deleteList t k-  _purge t = withReaderT _databaseClient $ _purge t-  _commit t = withReaderT _databaseClient $ _commit t-- -- | Main function, meant to be used in your personal configuration file. -- -- Here is an example: -- -- > import           Imm.Boot--- > import           Imm.Database.JsonFile+-- > import           Imm.Database.JsonFile as Database -- > import           Imm.Feed--- > import           Imm.Hooks.SendMail--- > import           Imm.HTTP.Conduit--- > import           Imm.Logger.Simple--- > import           Imm.XML.Simple+-- > 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   <- defaultLogger--- >   manager  <- defaultManager--- >   database <- defaultDatabase+-- >   logger     <- Logger.mkHandle <$> defaultLogger+-- >   database   <- Database.mkHandle <$> defaultDatabase+-- >   httpClient <- HTTP.mkHandle <$> defaultManager -- >--- >   imm $ mkModulesM manager database logger sendmail defaultXmlParser+-- >   imm logger database httpClient hooks xmlParser -- >--- > sendmail :: SendMailSettings--- > sendmail = SendMailSettings smtpServer formatMail+-- > 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" } )@@ -129,35 +76,34 @@ -- > smtpServer _ _ = SMTPServer -- >   (Just $ Authentication PLAIN "user" "password") -- >   (StartTls "smtp.host" defaultSettingsSMTPSTARTTLS)-imm :: ModulesM IO -> IO ()-imm modules = void $ do+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, modules)+  Dyre.wrap (optionDyreMode options) realMain (optionCommand options, optionLogLevel options, optionColorizeLogs options, logger, database, httpClient, hooks, xmlParser) -realMain :: (MonadAsync m, MonadTime m, MonadCatch m)-         => (Command, LogLevel, Bool, ModulesM m) -> m ()-realMain (command, logLevel, enableColors, ModulesM modules) = void $ flip runReaderT modules $ do-  setColorizeLogs enableColors-  setLogLevel logLevel-  logDebug . ("Dynamic reconfiguration settings:" <++>) . indent 2 =<< Dyre.describePaths-  logDebug $ "Executing: " <> pretty command-  logDebug . ("Using database:" <++>) . indent 2 =<< _describeDatabase FeedTable+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 (logError . pretty . displayException) $ case command of-    Check t        -> Core.check =<< resolveTarget ByPassConfirmation t+  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 stdin-    Read t         -> mapM_ Database.markAsRead =<< resolveTarget AskConfirmation t-    Run t          -> Core.run =<< resolveTarget ByPassConfirmation t-    Show t         -> Core.showFeed =<< resolveTarget ByPassConfirmation t+    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 u c-    Unread t       -> mapM_ Database.markAsUnread =<< resolveTarget AskConfirmation t-    Unsubscribe t  -> Database.deleteList FeedTable =<< resolveTarget AskConfirmation t+    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 FeedTable-  flushLogs+  Database.commit logger database+  flushLogs logger   -- * Util@@ -177,14 +123,13 @@   unless (null x || x == ("Y" :: Text)) $ throwM InterruptedException  -resolveTarget :: (MonadBase IO m, MonadThrow m, MonadDatabase FeedTable m)-              => SafeGuard -> Maybe Core.FeedRef -> m [FeedID]-resolveTarget s Nothing = do-  result <- keys <$> Database.fetchAll FeedTable+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 _ (Just (ByUID i)) = do-  result <- fst . (!! i) . mapToList <$> Database.fetchAll FeedTable-  -- logInfo $ "Target(s): " <> show (pretty 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]+resolveTarget _ _ (Just (ByURI uri)) = return [FeedID uri]
src/lib/Imm/Core.hs view
@@ -18,18 +18,16 @@ ) where  -- {{{ Imports-import           Imm.Database                (MonadDatabase) import qualified Imm.Database                as Database import           Imm.Database.FeedTable import qualified Imm.Database.FeedTable      as Database import           Imm.Feed import           Imm.Hooks                   as Hooks-import           Imm.HTTP                    (MonadHttpClient) import qualified Imm.HTTP                    as HTTP-import           Imm.Logger+import           Imm.Logger as Logger import           Imm.Prelude import           Imm.Pretty-import           Imm.XML+import           Imm.XML as XML  import           Control.Concurrent.STM      (STM, atomically) import           Control.Concurrent.STM.TVar@@ -60,105 +58,103 @@   putStrLn $ "compiled by " <> Text.pack compilerName <> "-" <> Text.pack (showVersion compilerVersion)  -- | Print database status for given feed(s)-showFeed :: (MonadLog m, MonadThrow m, MonadDatabase FeedTable m)-         => [FeedID] -> m ()-showFeed feedIDs = do-  entries <- Database.fetchList FeedTable feedIDs-  flushLogs-  when (null entries) $ logWarning "No subscription"+showFeed :: MonadThrow m => Logger.Handle m -> Database.Handle m FeedTable -> [FeedID] -> m ()+showFeed logger database feedIDs = do+  entries <- Database.fetchList database feedIDs+  flushLogs logger+  when (null entries) $ log logger Warning "No subscription"   forM_ (zip [1..] $ Map.elems entries) $ \(i, entry) ->-    logInfo $ pretty (i :: Int) <+> prettyDatabaseEntry entry+    log logger Info $ pretty (i :: Int) <+> prettyDatabaseEntry entry  -- | Register the given feed URI in database-subscribe :: (MonadLog m, MonadDatabase FeedTable m, MonadCatch m)-          => URI -> Set Text -> m ()-subscribe uri = Database.register (FeedID uri)+subscribe :: MonadCatch m => Logger.Handle m -> Database.Handle m FeedTable -> URI -> Set Text -> m ()+subscribe logger database uri = Database.register logger database (FeedID uri)  -- | Check for unread elements without processing them-check :: (MonadAsync m, MonadCatch m, MonadLog m, MonadDatabase FeedTable m, MonadHttpClient m, MonadXmlParser m)-      => [FeedID] -> m ()-check feedIDs = do+check :: (MonadAsync m, MonadCatch m)+      => Logger.Handle m -> Database.Handle m FeedTable -> HTTP.Handle m -> XML.Handle m -> [FeedID] -> m ()+check logger database httpClient xmlParser feedIDs = do   progress <- liftBase $ newTVarIO 0    results <- Stream.toList $ wAsyncly $ do     feedID <- Stream.fromFoldable feedIDs-    result <- lift $ tryAny $ checkOne feedID+    result <- lift $ tryAny $ checkOne logger database httpClient xmlParser feedID     let logResult = either (red . pretty . displayException) (\n -> green (pretty n) <+> "new element(s)") result     n <- liftBase $ atomically $ do       modifyTVar (progress :: TVar Int) (+ 1)       readTVar progress-    lift $ logInfo $ brackets (fill width (bold $ cyan $ pretty n) <+> "/" <+> pretty total) <+> "Checked" <+> magenta (pretty feedID) <+> "=>" <+> logResult+    lift $ log logger Info $ brackets (fill width (bold $ cyan $ pretty n) <+> "/" <+> pretty total) <+> "Checked" <+> magenta (pretty feedID) <+> "=>" <+> logResult     return result -  flushLogs+  flushLogs logger    let (failures, successes) = partitionEithers $ zipWith (\a -> bimap (a,) (a,)) feedIDs results-  unless (null failures) $ logError $ bold (pretty $ length failures) <+> "feeds in error"-  logInfo $ bold (pretty $ sum $ map snd successes) <+> "new element(s) overall"+  unless (null failures) $ log logger Error $ bold (pretty $ length failures) <+> "feeds in error"+  log logger Info $ bold (pretty $ sum $ map snd successes) <+> "new element(s) overall"    where width = length (show total :: String)         total = length feedIDs -checkOne :: (MonadBase IO m, MonadCatch m, MonadLog m, MonadDatabase FeedTable m, MonadHttpClient m, MonadXmlParser m)-         => FeedID -> m Int-checkOne feedID = do-  feed <- getFeed feedID+checkOne :: (MonadBase IO m, MonadCatch m)+         => Logger.Handle m -> Database.Handle m FeedTable -> HTTP.Handle m -> XML.Handle m -> FeedID -> m Int+checkOne logger database httpClient xmlParser feedID = do+  feed <- getFeed logger httpClient xmlParser feedID   case feed of-    Atom _ -> logDebug $ "Parsed Atom feed: " <> pretty feedID-    Rss _  -> logDebug $ "Parsed RSS feed: " <> pretty feedID+    Atom _ -> log logger Debug $ "Parsed Atom feed: " <> pretty feedID+    Rss _  -> log logger Debug $ "Parsed RSS feed: " <> pretty feedID    let dates = mapMaybe getDate $ getElements feed -  logDebug $ vsep $ map prettyElement $ getElements feed-  status <- Database.getStatus feedID+  log logger Debug $ vsep $ map prettyElement $ getElements feed+  status <- Database.getStatus database feedID    return $ length $ filter (unread status) dates   where unread (LastUpdate t1) t2 = t2 > t1         unread _ _                = True  -run :: (MonadTime m, MonadAsync m, MonadCatch m, MonadImm m, MonadLog m, MonadDatabase FeedTable m, MonadHttpClient m, MonadXmlParser m)-    => [FeedID] -> m ()-run feedIDs = do+run :: (MonadTime m, MonadAsync m, MonadCatch m)+    => Logger.Handle m -> Database.Handle m FeedTable -> HTTP.Handle m -> Hooks.Handle m -> XML.Handle m -> [FeedID] -> m ()+run logger database httpClient hooks xmlParser feedIDs = do   progress <- liftBase $ newTVarIO 0    results <- Stream.toList $ wAsyncly $ do     feedID <- Stream.fromFoldable feedIDs-    result <- lift $ tryAny $ runOne feedID+    result <- lift $ tryAny $ runOne logger database httpClient hooks xmlParser feedID     let logResult = either (red . pretty . displayException) (\n -> green (pretty n) <+> "new element(s)") result     n <- liftBase $ atomically $ do       modifyTVar progress (+ 1)       readTVar progress :: STM Int-    lift $ logInfo $ brackets (fill width (bold $ cyan $ pretty n) <+> "/" <+> pretty total) <+> "Processed" <+> magenta (pretty feedID) <+> "=>" <+> logResult+    lift $ log logger Info $ brackets (fill width (bold $ cyan $ pretty n) <+> "/" <+> pretty total) <+> "Processed" <+> magenta (pretty feedID) <+> "=>" <+> logResult     return $ bimap (feedID,) (feedID,) result -  flushLogs+  flushLogs logger    let (failures, successes) = partitionEithers results -  unless (null failures) $ logError $ bold (pretty $ length failures) <+> "feeds in error"-  logInfo $ bold (pretty $ sum $ map snd successes) <+> "new element(s) overall"+  unless (null failures) $ log logger Error $ bold (pretty $ length failures) <+> "feeds in error"+  log logger Info $ bold (pretty $ sum $ map snd successes) <+> "new element(s) overall"    where width = length (show total :: String)         total = length feedIDs -runOne :: (MonadTime m, MonadCatch m, MonadImm m, MonadLog m, MonadDatabase FeedTable m, MonadHttpClient m, MonadXmlParser m)-       => FeedID -> m Int-runOne feedID = do-  feed <- getFeed feedID-  unreadElements <- filterM (fmap not . isRead feedID) $ getElements feed+runOne :: (MonadTime m, MonadCatch m)+       => Logger.Handle m -> Database.Handle m FeedTable -> HTTP.Handle m -> Hooks.Handle m -> XML.Handle m -> FeedID -> m Int+runOne logger database httpClient hooks xmlParser feedID = do+  feed <- getFeed logger httpClient xmlParser feedID+  unreadElements <- filterM (fmap not . isRead database feedID) $ getElements feed    forM_ unreadElements $ \element -> do-    onNewElement feed element-    mapM_ (Database.addReadHash feedID) $ getHashes element+    onNewElement logger hooks feed element+    mapM_ (Database.addReadHash logger database feedID) $ getHashes element -  Database.markAsRead feedID+  Database.markAsRead logger database feedID   return $ length unreadElements  -isRead :: (MonadCatch m, MonadDatabase FeedTable m) => FeedID -> FeedElement -> m Bool-isRead feedID element = do-  DatabaseEntry _ _ readHashes lastCheck <- Database.fetch FeedTable feedID+isRead :: MonadCatch m => Database.Handle m FeedTable -> FeedID -> FeedElement -> m Bool+isRead database feedID element = do+  DatabaseEntry _ _ readHashes lastCheck <- Database.fetch database feedID   let matchHash = not $ null $ (setFromList (getHashes element) :: Set Int) `intersection` readHashes       matchDate = case (lastCheck, getDate element) of         (Nothing, _)     -> False@@ -167,19 +163,16 @@   return $ matchHash || matchDate  -- | 'subscribe' to all feeds described by the OPML document provided in input-importOPML :: (MonadLog m, MonadDatabase FeedTable m, MonadCatch m)-           => ConduitT () ByteString m () -> m ()-importOPML input = do+importOPML :: MonadCatch m => Logger.Handle m -> Database.Handle m FeedTable -> ConduitT () ByteString m () -> m ()+importOPML logger database input = do   opml <- runConduit $ input .| XML.parseBytes def .| force "Invalid OPML" parseOpml-  forM_ (opmlOutlines opml) $ importOPML' mempty+  forM_ (opmlOutlines opml) $ importOPML' logger database mempty -importOPML' :: (MonadLog m, MonadDatabase FeedTable m, MonadCatch m)-            => Set Text -> Tree OpmlOutline -> m ()-importOPML' _ (Node (OpmlOutlineGeneric b _) sub) = mapM_ (importOPML' (Set.singleton . toNullable $ OPML.text b)) sub-importOPML' c (Node (OpmlOutlineSubscription _ s) _) = subscribe (xmlUri s) c-importOPML' _ _ = return ()+importOPML' :: MonadCatch m => Logger.Handle m -> Database.Handle m FeedTable -> Set Text -> Tree OpmlOutline -> m ()+importOPML' logger database _ (Node (OpmlOutlineGeneric b _) sub) = mapM_ (importOPML' logger database (Set.singleton . toNullable $ OPML.text b)) sub+importOPML' logger database c (Node (OpmlOutlineSubscription _ s) _) = subscribe logger database (xmlUri s) c+importOPML' _ _ _ _ = return ()  -getFeed :: (MonadCatch m, MonadHttpClient m, MonadLog m, MonadXmlParser m)-        => FeedID -> m Feed-getFeed (FeedID uri) = HTTP.get uri >>= parseXml uri+getFeed :: MonadCatch m => Logger.Handle m -> HTTP.Handle m -> XML.Handle m -> FeedID -> m Feed+getFeed logger httpClient xmlParser (FeedID uri) = HTTP.get logger httpClient uri >>= parseXml xmlParser uri
src/lib/Imm/Database.hs view
@@ -3,14 +3,20 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude     #-} {-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE RankNTypes            #-} {-# LANGUAGE StandaloneDeriving    #-} {-# LANGUAGE TypeFamilies          #-} {-# LANGUAGE UndecidableInstances  #-} -- | Database module abstracts over a key-value database that supports CRUD operations.+--+-- This module follows the [Handle pattern](https://jaspervdj.be/posts/2018-03-08-handle-pattern.html).+--+-- > import qualified Imm.Database as Database module Imm.Database where  -- {{{ Imports-import           Imm.Logger+import qualified Imm.Logger as Logger+import           Imm.Logger hiding(Handle) import           Imm.Prelude import           Imm.Pretty @@ -24,17 +30,18 @@   => Table t where   type Key t :: *   type Entry t :: *+  rep :: t --- | Monad capable of interacting with a key-value store.-class MonadThrow m => MonadDatabase t m where-  _describeDatabase :: t -> m (Doc a)-  _fetchList :: t -> [Key t] -> m (Map (Key t) (Entry t))-  _fetchAll :: t -> m (Map (Key t) (Entry t))-  _update :: t -> Key t -> (Entry t -> Entry t) -> m ()-  _insertList :: t -> [(Key t, Entry t)] -> m ()-  _deleteList :: t -> [Key t] -> m ()-  _purge :: t -> m ()-  _commit :: t -> m ()+data Handle m t = Handle+  { _describeDatabase :: forall a . m (Doc a)+  , _fetchList :: [Key t] -> m (Map (Key t) (Entry t))+  , _fetchAll :: m (Map (Key t) (Entry t))+  , _update :: Key t -> (Entry t -> Entry t) -> m ()+  , _insertList :: [(Key t, Entry t)] -> m ()+  , _deleteList :: [Key t] -> m ()+  , _purge :: m ()+  , _commit :: m ()+  }   data DatabaseException t@@ -64,43 +71,46 @@  -- * Primitives -fetch :: (MonadDatabase t m, Table t, MonadThrow m) => t -> Key t -> m (Entry t)-fetch t k = do-  results <- _fetchList t [k]-  maybe (throwM $ NotFound t [k]) return $ lookup k results+fetch :: Monad m => Table t => MonadThrow m => Handle m t -> Key t -> m (Entry t)+fetch handle k = do+  results <- _fetchList handle [k]+  maybe (throwM $ NotFound (table handle) [k]) return $ lookup k results -fetchList :: (MonadDatabase t m, MonadThrow m) => t -> [Key t] -> m (Map (Key t) (Entry t))+fetchList :: Monad m => Handle m t -> [Key t] -> m (Map (Key t) (Entry t)) fetchList = _fetchList -fetchAll :: (MonadThrow m, MonadDatabase t m) => t -> m (Map (Key t) (Entry t))+fetchAll :: Monad m => Handle m t -> m (Map (Key t) (Entry t)) fetchAll = _fetchAll -update :: (MonadDatabase t m, MonadThrow m) => t -> Key t -> (Entry t -> Entry t) -> m ()+update :: Monad m => Handle m t -> Key t -> (Entry t -> Entry t) -> m () update  = _update -insert :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> Key t -> Entry t -> m ()-insert t k v = insertList t [(k, v)]+insert :: Monad m => Logger.Handle m -> Handle m t -> Key t -> Entry t -> m ()+insert logger handle k v = insertList logger handle [(k, v)] -insertList :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> [(Key t, Entry t)] -> m ()-insertList t i = do-  logInfo $ "Inserting " <> yellow (pretty $ length i) <> " entries..."-  _insertList t i+insertList :: Monad m => Logger.Handle m -> Handle m t -> [(Key t, Entry t)] -> m ()+insertList logger handle i = do+  log logger Info $ "Inserting " <> yellow (pretty $ length i) <> " entries..."+  _insertList handle i -delete :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> Key t -> m ()-delete t k = deleteList t [k]+delete :: Monad m => Logger.Handle m -> Handle m t -> Key t -> m ()+delete logger handle k = deleteList logger handle [k] -deleteList :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> [Key t] -> m ()-deleteList t k = do-  logInfo $ "Deleting " <> yellow (pretty $ length k) <> " entries..."-  _deleteList t k+deleteList :: Monad m => Logger.Handle m -> Handle m t -> [Key t] -> m ()+deleteList logger handle k = do+  log logger Info $ "Deleting " <> yellow (pretty $ length k) <> " entries..."+  _deleteList handle k -purge :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> m ()-purge t = do-  logInfo "Purging database..."-  _purge t+purge :: Monad m => Logger.Handle m -> Handle m t -> m ()+purge logger handle = do+  log logger Info "Purging database..."+  _purge handle -commit :: (MonadThrow m, MonadDatabase t m, MonadLog m) => t -> m ()-commit t = do-  logDebug "Committing database transaction..."-  _commit t-  logDebug "Database transaction committed"+commit :: Monad m => Logger.Handle m -> Handle m t -> m ()+commit logger handle = do+  log logger Debug "Committing database transaction..."+  _commit handle+  log logger Debug "Database transaction committed"++table :: Table t => Handle m t -> t+table _ = rep
src/lib/Imm/Database/FeedTable.hs view
@@ -7,8 +7,8 @@  -- {{{ Imports import           Imm.Aeson-import           Imm.Database-import           Imm.Logger+import           Imm.Database as Database+import           Imm.Logger as Logger import           Imm.Prelude import           Imm.Pretty @@ -80,6 +80,7 @@ instance Table FeedTable where   type Key FeedTable = FeedID   type Entry FeedTable = DatabaseEntry+  rep = FeedTable   data FeedStatus = Unknown | New | LastUpdate UTCTime@@ -95,37 +96,33 @@  -- * Primitives -register :: (MonadThrow m, MonadLog m, MonadDatabase FeedTable m)-          => FeedID -> Set Text -> m ()-register feedID tags = do-  logInfo $ "Registering feed" <+> magenta (pretty feedID) <> "..."-  insert FeedTable feedID $ newDatabaseEntry feedID tags+register :: MonadThrow m => Logger.Handle m -> Database.Handle m FeedTable -> FeedID -> Set Text -> m ()+register logger database feedID tags = do+  log logger Info $ "Registering feed" <+> magenta (pretty feedID) <> "..."+  insert logger database feedID $ newDatabaseEntry feedID tags -getStatus :: (MonadDatabase FeedTable m, MonadCatch m)-          => FeedID -> m FeedStatus-getStatus feedID = handleAny (\_ -> return Unknown) $ do-  result <- fmap Just (fetch FeedTable feedID) `catchAny` (\_ -> return Nothing)+getStatus :: MonadCatch m => Database.Handle m FeedTable -> FeedID -> m FeedStatus+getStatus database feedID = handleAny (\_ -> return Unknown) $ do+  result <- fmap Just (fetch database feedID) `catchAny` (\_ -> return Nothing)   return $ maybe New LastUpdate $ entryLastCheck =<< result -addReadHash :: (MonadDatabase FeedTable m, MonadThrow m, MonadLog m)-               => FeedID -> Int -> m ()-addReadHash feedID hash = do-  logDebug $ "Adding read hash:" <+> pretty hash <> "..."-  update FeedTable feedID f+addReadHash :: MonadThrow m => Logger.Handle m -> Database.Handle m FeedTable -> FeedID -> Int -> m ()+addReadHash logger database feedID hash = do+  log logger Debug $ "Adding read hash:" <+> pretty hash <> "..."+  update database feedID f   where f a = a { entryReadHashes = insertSet hash $ entryReadHashes a }  -- | Set the last check time to now-markAsRead :: (MonadTime m, MonadDatabase FeedTable m, MonadThrow m, MonadLog m)-           => FeedID -> m ()-markAsRead feedID = do-  logDebug $ "Marking feed as read:" <+> pretty feedID <> "..."+markAsRead :: (MonadTime m, MonadThrow m)+           => Logger.Handle m -> Database.Handle m FeedTable -> FeedID -> m ()+markAsRead logger database feedID = do+  log logger Debug $ "Marking feed as read:" <+> pretty feedID <> "..."   utcTime <- currentTime-  update FeedTable feedID (f utcTime)+  update database feedID (f utcTime)   where f time a = a { entryLastCheck = Just time }  -- | Unset feed's last update and remove all read hashes-markAsUnread :: (MonadDatabase FeedTable m, MonadThrow m, MonadLog m)-             => FeedID -> m ()-markAsUnread feedID = do-  logInfo $ "Marking feed as unread:" <+> prettyFeedID feedID <> "..."-  update FeedTable feedID $ \a -> a { entryReadHashes = mempty, entryLastCheck = Nothing }+markAsUnread :: MonadThrow m => Logger.Handle m -> Database.Handle m FeedTable -> FeedID -> m ()+markAsUnread logger database feedID = do+  log logger Info $ "Marking feed as unread:" <+> prettyFeedID feedID <> "..."+  update database feedID $ \a -> a { entryReadHashes = mempty, entryLastCheck = Nothing }
src/lib/Imm/Database/JsonFile.hs view
@@ -10,6 +10,7 @@   ( JsonFileDatabase   , mkJsonFileDatabase   , defaultDatabase+  , mkHandle   , JsonException(..)   , module Imm.Database.FeedTable   ) where@@ -23,8 +24,6 @@ import           Imm.Pretty  import           Control.Concurrent.MVar.Lifted-import           Control.Monad.Reader.Class-import           Control.Monad.Trans.Reader     (ReaderT) import           Data.Aeson import           Data.ByteString.Lazy           (hPut) import           Data.ByteString.Streaming      (hGetContents, toLazy_)@@ -61,25 +60,21 @@   displayException _ = "Unable to parse JSON"  -instance (Table t, FromJSON (Key t), FromJSON (Entry t), ToJSON (Key t), ToJSON (Entry t))-  => MonadDatabase t (ReaderT (MVar (JsonFileDatabase t)) IO) where-  _describeDatabase _ = pretty <$> (readMVar =<< ask)-  _fetchList t keys = Map.filterWithKey (\uri _ -> member uri $ Set.fromList keys) <$> fetchAll t-  _fetchAll _ = do-    mvar <- ask-    lift $ modifyMVar mvar $ \database -> do-      a@(JsonFileDatabase _ cache _) <- loadInCache database-      return (a, cache)-  _update _ key f = exec (\a -> update a key f)-  _insertList _ rows = exec $ insert rows-  _deleteList _ keys = exec $ delete keys-  _purge _ = exec purge-  _commit _ = exec commit--exec :: (a -> IO a) -> ReaderT (MVar a) IO ()-exec f = do-  mvar <- ask-  lift $ modifyMVar_ mvar f+mkHandle :: (Table t, FromJSON (Key t), FromJSON (Entry t), ToJSON (Key t), ToJSON (Entry t), MonadBase IO m)+         => MVar (JsonFileDatabase t) -> Handle m t+mkHandle mvar = Handle+  { _describeDatabase = pretty <$> readMVar mvar+  , _fetchList = \keys -> Map.filterWithKey (\uri _ -> member uri $ Set.fromList keys) <$> fetchAll_+  , _fetchAll = fetchAll_+  , _update = \key f -> liftBase $ modifyMVar_ mvar (\a -> update a key f)+  , _insertList = liftBase . modifyMVar_ mvar . insert+  , _deleteList = liftBase . modifyMVar_ mvar . delete+  , _purge = liftBase $ modifyMVar_ mvar purge+  , _commit = liftBase $ modifyMVar_ mvar commit+  }+  where fetchAll_ = liftBase $ modifyMVar mvar $ \database -> do+          a@(JsonFileDatabase _ cache _) <- loadInCache database+          return (a, cache)   -- * Low-level implementation@@ -132,6 +127,6 @@        => JsonFileDatabase t -> IO (JsonFileDatabase t) commit t@(JsonFileDatabase file cache status) = case status of   Dirty -> do-    withFile file WriteMode $ \h -> (hPut h $ encode $ Map.toList cache)+    withFile file WriteMode $ \h -> hPut h $ encode $ Map.toList cache     return $ JsonFileDatabase file cache Clean   _ -> return t
src/lib/Imm/HTTP.hs view
@@ -3,10 +3,15 @@ {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} -- | HTTP module abstracts over HTTP requests to the external world.+--+-- This module follows the [Handle pattern](https://jaspervdj.be/posts/2018-03-08-handle-pattern.html).+--+-- > import qualified Imm.HTTP as HTTP module Imm.HTTP where  -- {{{ Imports-import           Imm.Logger+import qualified Imm.Logger as Logger+import           Imm.Logger hiding(Handle) import           Imm.Prelude import           Imm.Pretty @@ -15,15 +20,16 @@  -- * Types --- | Monad capable of performing GET HTTP requests.-class MonadThrow m => MonadHttpClient m where-  httpGet :: URI -> m LByteString+-- | Handle to perform GET HTTP requests.+newtype Handle m = Handle+  { httpGet :: URI -> m LByteString+  } + -- * Primitives  -- | Simple wrapper around 'httpGet' that also logs the requested URI.-get :: (MonadHttpClient m, MonadLog m, MonadThrow m)-    => URI -> m LByteString-get uri = do-  logDebug $ "Fetching " <> prettyURI uri-  httpGet uri+get :: Monad m => Logger.Handle m -> Handle m -> URI -> m LByteString+get logger handle uri = do+  log logger Debug $ "Fetching " <> prettyURI uri+  httpGet handle uri
src/lib/Imm/HTTP/Simple.hs view
@@ -3,14 +3,13 @@ {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} -- | Implementation of "Imm.HTTP" based on "Network.HTTP.Client".-module Imm.HTTP.Simple (defaultManager, module Reexport) where+module Imm.HTTP.Simple (mkHandle, defaultManager, module Reexport) where  -- {{{ Imports import           Imm.HTTP import           Imm.Prelude import           Imm.Pretty -import           Control.Monad.Trans.Reader import           Data.CaseInsensitive import           Network.Connection         as Reexport import           Network.HTTP.Client        as Reexport@@ -18,11 +17,10 @@ import           URI.ByteString -- }}} --- | Monad capable of performing HTTP GET requests.-instance MonadHttpClient (ReaderT Manager IO) where-  httpGet uri = do-    manager <- ask-    lift $ httpGet' manager uri+mkHandle :: MonadBase IO m => Manager -> Handle m+mkHandle manager = Handle+  { httpGet = liftBase . httpGet' manager+  }  -- | Default manager uses TLS and no proxy defaultManager :: IO Manager@@ -37,7 +35,7 @@     -- codec'   <- reader $ view (config.codec)     -- return $ response $=+ decode codec' -parseRequest' :: (MonadThrow m) => URI -> m Request+parseRequest' :: MonadThrow m => URI -> m Request parseRequest' = parseRequest . show . prettyURI  -- | Build an HTTP request for given URI
src/lib/Imm/Hooks.hs view
@@ -1,27 +1,29 @@-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-}--- | Hooks module to define the main behavior of the program.+-- | Hooks module abstracts over the main behavior of the program.+--+-- This module follows the [Handle pattern](https://jaspervdj.be/posts/2018-03-08-handle-pattern.html).+--+-- > import qualified Imm.Hooks as Hooks module Imm.Hooks where  -- {{{ Imports import           Imm.Feed-import           Imm.Logger+import qualified Imm.Logger as Logger+import           Imm.Logger hiding(Handle) import           Imm.Prelude import           Imm.Pretty -- }}}  -- * Types --- | Monad capable of acting on specific events.-class Monad m => MonadImm m where-  -- | Action triggered for each unread feed element-  processNewElement :: Feed -> FeedElement -> m ()+newtype Handle m = Handle+  { processNewElement :: Feed -> FeedElement -> m ()  -- ^ Action triggered for each unread feed element+  }  -- * Primitives -onNewElement :: (MonadImm m, MonadLog m) => Feed -> FeedElement -> m ()-onNewElement feed element = do-  logDebug $ "Unread element:" <+> pretty (getTitle element)-  processNewElement feed element+onNewElement :: Monad m => Logger.Handle m -> Handle m -> Feed -> FeedElement -> m ()+onNewElement logger handle feed element = do+  log logger Debug $ "Unread element:" <+> pretty (getTitle element)+  processNewElement handle feed element
src/lib/Imm/Hooks/Dummy.hs view
@@ -1,21 +1,21 @@-{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} -- | Implementation of "Imm.Hooks" that does nothing, -- except suggesting the user to define proper hooks. -- -- This is the default implementation of the program.-module Imm.Hooks.Dummy where+module Imm.Hooks.Dummy (module Imm.Hooks.Dummy, module Imm.Hooks) where  -- {{{ Imports import           Imm.Hooks import           Imm.Prelude  import           Control.Exception-import           Control.Monad.Trans.Reader -- }}}  data DummyHooks = DummyHooks -instance MonadImm (ReaderT DummyHooks IO) where-  processNewElement _ _ = throwM $ NoMethodError "Please define a valid Imm.Hooks.processNewElement function"+mkHandle :: MonadThrow m => Handle m+mkHandle = Handle+  { processNewElement = \_ _ -> throwM $ NoMethodError "Please define a valid Imm.Hooks.processNewElement function"+  }
src/lib/Imm/Hooks/SendMail.hs view
@@ -1,7 +1,6 @@-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts  #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE TypeOperators     #-} -- | Implementation of "Imm.Hooks" that sends a mail via a SMTP server for each new RSS/Atom element. -- You may want to check out "Network.HaskellNet.SMTP", "Network.HaskellNet.SMTP.SSL" and "Network.Mail.Mime" modules for additional information. --@@ -30,7 +29,6 @@ import           Imm.Prelude import           Imm.Pretty -import           Control.Monad.Trans.Reader import           Data.NonNull import           Data.Time import           Network.HaskellNet.SMTP     as Reexport@@ -68,13 +66,14 @@  data SendMailSettings = SendMailSettings (Feed -> FeedElement -> SMTPServer) FormatMail -instance MonadImm (ReaderT SendMailSettings IO) where-  processNewElement feed element = do-    SendMailSettings connectionSettings formatMail <- ask-    timezone <- lift getCurrentTimeZone-    currentTime <- lift getCurrentTime-    let mail = buildMail formatMail currentTime timezone feed element-    lift $ withSMTPConnection (connectionSettings feed element) $ sendMimeMail2 mail+mkHandle :: MonadBase IO m => SendMailSettings -> Handle m+mkHandle (SendMailSettings connectionSettings formatMail) = Handle+  { processNewElement = \feed element -> do+      timezone <- liftBase getCurrentTimeZone+      currentTime <- liftBase getCurrentTime+      let mail = buildMail formatMail currentTime timezone feed element+      liftBase $ withSMTPConnection (connectionSettings feed element) $ sendMimeMail2 mail+  }   -- * Default behavior
src/lib/Imm/Hooks/WriteFile.hs view
@@ -1,5 +1,4 @@-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE OverloadedStrings #-} -- | Implementation of "Imm.Hooks" that writes a file for each new RSS/Atom item.@@ -12,7 +11,6 @@ import           Imm.Pretty  import           Control.Arrow-import           Control.Monad.Trans.Reader import           Data.ByteString.Builder import           Data.ByteString.Streaming     (toStreamingByteString) import           Data.Monoid.Textual           hiding (elem, map)@@ -37,12 +35,13 @@  newtype WriteFileSettings = WriteFileSettings (Feed -> FeedElement -> FileInfo) -instance MonadImm (ReaderT WriteFileSettings IO) where-  processNewElement feed element = do-    WriteFileSettings f <- ask-    let FileInfo path content = f feed element-    lift $ createDirectoryIfMissing True $ takeDirectory path-    writeBinaryFile path $ toStreamingByteString content+mkHandle :: MonadBase IO m => MonadIO m => MonadMask m => WriteFileSettings -> Handle m+mkHandle (WriteFileSettings f) = Handle+  { processNewElement = \feed element -> do+      let FileInfo path content = f feed element+      liftBase $ createDirectoryIfMissing True $ takeDirectory path+      writeBinaryFile path $ toStreamingByteString content+  }  -- * Default behavior 
src/lib/Imm/Logger.hs view
@@ -3,7 +3,12 @@ {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude     #-} {-# LANGUAGE OverloadedStrings     #-}--- | Logger module.+-- | Logger module abstracts over logging data.+--+-- This module follows the [Handle pattern](https://jaspervdj.be/posts/2018-03-08-handle-pattern.html).+--+-- > import qualified Imm.Logger as Logger (Handle)+-- > import Imm.Logger hiding (Handle) module Imm.Logger where  -- {{{ Imports@@ -13,6 +18,14 @@  -- * Types +data Handle m = Handle+  { log :: LogLevel -> Doc AnsiStyle -> m ()+  , getLogLevel :: m LogLevel+  , setLogLevel :: LogLevel -> m ()+  , setColorizeLogs :: Bool -> m ()+  , flushLogs :: m ()+  }+ data LogLevel = Debug | Info | Warning | Error   deriving(Eq, Ord, Read, Show) @@ -21,19 +34,3 @@   pretty Info    = "INFO"   pretty Warning = "WARNING"   pretty Error   = "ERROR"---- | Monad capable of logging pretty text.-class Monad m => MonadLog m where-  log :: LogLevel -> Doc AnsiStyle -> m ()-  getLogLevel :: m LogLevel-  setLogLevel :: LogLevel -> m ()-  setColorizeLogs :: Bool -> m ()-  flushLogs :: m ()---- * Helpers--logDebug, logInfo, logWarning, logError :: MonadLog m => Doc AnsiStyle -> m ()-logDebug = log Debug-logInfo = log Info-logWarning = log Warning-logError = log Error
src/lib/Imm/Logger/Simple.hs view
@@ -11,7 +11,7 @@ import           Imm.Pretty  import           Control.Concurrent.MVar.Lifted-import           Control.Monad.Trans.Reader+import           Control.Monad.Trans.Control import           Data.Text.Prettyprint.Doc.Render.Terminal import           System.Log.FastLogger                     as Reexport -- }}}@@ -23,6 +23,7 @@   , _colorizeLogs   :: Bool       -- ^ Enable log colorisation   } + -- | Default logger forwards error messages to stderr, and other messages to stdout. defaultLogger :: IO (MVar LoggerSettings) defaultLogger = newMVar =<< LoggerSettings@@ -31,30 +32,21 @@   <*> pure Info   <*> pure True -instance MonadLog (ReaderT (MVar LoggerSettings) IO) where-  -- log :: LogLevel -> Doc -> m ()-  log l t = do-    settings <- readMVar =<< ask-    let loggerSet = (if l == Error then _errorLoggerSet else _loggerSet) settings-        handleColor = (\c -> if c then id else unAnnotate) $ _colorizeLogs settings-        refLevel = _logLevel settings-    when (l >= refLevel) $ lift $ pushLogStrLn loggerSet $ toLogStr $ renderLazy $ layoutPretty defaultLayoutOptions $ handleColor t -  -- getLogLevel :: m LogLevel-  getLogLevel = _logLevel <$> (readMVar =<< ask)--  -- setLogLevel :: LogLevel -> m ()-  setLogLevel level = do-    mvar <- ask-    modifyMVar_ mvar $ \settings -> return (settings { _logLevel = level })--  -- setColorizeLogs :: Bool -> m ()-  setColorizeLogs value = do-    mvar <- ask-    modifyMVar_ mvar $ \settings -> return (settings { _colorizeLogs = value })+mkHandle :: MonadBaseControl IO m => MVar LoggerSettings -> Handle m+mkHandle settings = Handle+  { log = \l t -> do+      s <- readMVar settings+      let loggerSet = (if l == Error then _errorLoggerSet else _loggerSet) s+          handleColor = (\c -> if c then id else unAnnotate) $ _colorizeLogs s+          refLevel = _logLevel s+      when (l >= refLevel) $ liftBase $ pushLogStrLn loggerSet $ toLogStr $ renderLazy $ layoutPretty defaultLayoutOptions $ handleColor t -  -- flushLogs :: m ()-  flushLogs = do-    settings <- readMVar =<< ask-    lift $ flushLogStr $ _loggerSet settings-    lift $ flushLogStr $ _errorLoggerSet settings+  , getLogLevel = _logLevel <$> readMVar settings+  , setLogLevel = \level -> modifyMVar_ settings $ \s -> return (s { _logLevel = level })+  , setColorizeLogs = \value -> modifyMVar_ settings $ \s -> return (s { _colorizeLogs = value })+  , flushLogs = do+      s <- readMVar settings+      liftBase $ flushLogStr $ _loggerSet s+      liftBase $ flushLogStr $ _errorLoggerSet s+  }
src/lib/Imm/Prelude.hs view
@@ -3,7 +3,7 @@  -- {{{ Imports import           Control.Applicative             as X-import           Control.Exception.Safe          as X+import           Control.Exception.Safe          as X hiding(handle) import           Control.Monad                   as X (MonadPlus (..), unless,                                                        void, when) import           Control.Monad.Base              as X
src/lib/Imm/XML.hs view
@@ -1,5 +1,9 @@ {-# LANGUAGE NoImplicitPrelude #-} -- | XML module abstracts over the parsing of RSS/Atom feeds.+--+-- This module follows the [Handle pattern](https://jaspervdj.be/posts/2018-03-08-handle-pattern.html).+--+-- > import qualified Imm.XML as XML module Imm.XML where  -- {{{ Imports@@ -9,6 +13,6 @@ import           URI.ByteString -- }}} --- | Monad capable of parsing XML into a 'Feed' (RSS or Atom).-class MonadThrow m => MonadXmlParser m where-  parseXml :: URI -> LByteString -> m Feed+newtype Handle m = Handle+  { parseXml :: URI -> LByteString -> m Feed+  }
src/lib/Imm/XML/Conduit.hs view
@@ -2,7 +2,7 @@ {-# LANGUAGE NoImplicitPrelude #-} {-# LANGUAGE RankNTypes        #-} -- | Implementation of "Imm.XML" based on 'Conduit'.-module Imm.XML.Conduit where+module Imm.XML.Conduit (module Imm.XML.Conduit, module Imm.XML) where  -- {{{ Imports import           Imm.Feed@@ -11,7 +11,6 @@  import           Control.Monad import           Control.Monad.Fix-import           Control.Monad.Trans.Reader import           Data.Conduit import           Data.XML.Types import           Text.Atom.Conduit.Parse@@ -26,10 +25,10 @@ newtype XmlParser = XmlParser (forall m . Monad m => URI -> ConduitT Event Event m ())  -- | 'Conduit' based implementation-instance (MonadIO m, MonadCatch m) => MonadXmlParser (ReaderT XmlParser m) where-  parseXml uri bytestring = do-    XmlParser preProcess <- ask-    lift $ runConduit $ parseLBS def bytestring .| preProcess uri .| force "Invalid feed" ((fmap Atom <$> atomFeed) `orE` (fmap Rss <$> rssDocument) `orE` (fmap Rss <$> rss1Document))+mkHandle :: MonadIO m => MonadCatch m => XmlParser -> Handle m+mkHandle (XmlParser preProcess) = Handle+  { parseXml = \uri bytestring -> liftIO $ runConduit $ parseLBS def bytestring .| preProcess uri .| force "Invalid feed" ((fmap Atom <$> atomFeed) `orE` (fmap Rss <$> rssDocument) `orE` (fmap Rss <$> rss1Document))+  }  -- | Forward all 'Event's without any pre-process defaultXmlParser :: XmlParser