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 +2/−2
- imm.cabal +2/−2
- src/bin/Executable.hs +17/−11
- src/lib/Imm.hs +4/−4
- src/lib/Imm/Boot.hs +47/−102
- src/lib/Imm/Core.hs +54/−61
- src/lib/Imm/Database.hs +49/−39
- src/lib/Imm/Database/FeedTable.hs +23/−26
- src/lib/Imm/Database/JsonFile.hs +17/−22
- src/lib/Imm/HTTP.hs +15/−9
- src/lib/Imm/HTTP/Simple.hs +6/−8
- src/lib/Imm/Hooks.hs +14/−12
- src/lib/Imm/Hooks/Dummy.hs +5/−5
- src/lib/Imm/Hooks/SendMail.hs +9/−10
- src/lib/Imm/Hooks/WriteFile.hs +8/−9
- src/lib/Imm/Logger.hs +14/−17
- src/lib/Imm/Logger/Simple.hs +18/−26
- src/lib/Imm/Prelude.hs +1/−1
- src/lib/Imm/XML.hs +7/−3
- src/lib/Imm/XML/Conduit.hs +5/−6
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