-- Hoogle documentation, generated by Haddock
-- See Hoogle, http://www.haskell.org/hoogle/
-- | Haskell binding to Notmuch, the mail indexer
--
-- Binding to the notmuch mail indexer, providing a hopefully somewhat
-- typesafe way to search your email.
@package notmuch
@version 0.3.1
-- | Stuff that we don't want to export by default, but that we do want to
-- expose in the library interface.
module Notmuch.Util
type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)
type Prism' s a = Prism s s a a
type Lens' a b = forall f. Functor f => (b -> f b) -> (a -> f a)
review :: MonadReader b m => Prism' t b -> m t
-- | Variant of bracket that works with ExceptT and allows resource
-- acquisition to fail, propagating the error. If resource finalisation
-- fails, the error is discarded.
bracketT :: (MonadError e m, MonadIO m) => ExceptT e IO a -> (a -> ExceptT e IO b) -> (a -> ExceptT e IO c) -> m c
-- | High-level interface to the notmuch mail indexer.
--
-- Example program to add/remove a tag on all messages matching a query:
--
--
-- main :: IO ()
-- main = getArgs >>= \args -> case args of
-- [path, expr, '+':tag] -> go path expr tag messageAddTag
-- [path, expr, '-':tag] -> go path expr tag messageRemoveTag
-- _ -> die "usage: hs-notmuch-tag-set DB-DIR SEARCH-TERM +TAG|-TAG"
-- where
-- go path expr tag f =
-- runExceptT (do
-- db <- databaseOpen path
-- query db (Bare expr) >>= messages >>= traverse_ (f (fromString tag))
-- ) >>= either (die . (show :: Status -> String)) pure
--
--
-- File descriptor exhaustion
--
-- Some Message operations cause the message file to be opened
-- (and remain open until the object gets garbage collected):
--
--
-- - messageHeader will open the file to read the headers,
-- except for the From, Subject and Message-Id
-- headers which are indexed.
--
--
-- If the RTS is using a multi-generation collector (the default), and if
-- you are working with lots of messages, you may hit max open
-- files limits. The best way to avoid this is to avoid the scenarios
-- outlined above. Alternative approaches that could help include:
--
--
-- - Use a single-generation collector (build with -rtsopts
-- and run with +RTS -G1). This incurs the cost of scanning the
-- entire heap on every GC run.
-- - In an interactive program, build with -threaded to enable
-- parallel GC. By default, major GC will be triggered when the program
-- is idle for a certain time.
-- - Manually execute performMajorGC at relevant times to ensure
-- that older generations get cleaned up.
--
module Notmuch
-- | Open a database. The database will be closed and associated resources
-- freed upon garbage collection.
--
-- The mode is determined by usage. Because read-only functions also work
-- on read-write databases, databaseOpenReadOnly is also provided
-- for convenience.
databaseOpen :: (Mode a, AsNotmuchError e, MonadError e m, MonadIO m) => FilePath -> m (Database a)
-- | Convenience function for opening a database read-only
databaseOpenReadOnly :: (AsNotmuchError e, MonadError e m, MonadIO m) => FilePath -> m (Database RO)
-- | Get the path of the database
databasePath :: Database a -> FilePath
-- | Database format version of the given database.
databaseVersion :: MonadIO m => Database a -> m Int
-- | A database handle. The database will be closed and freed when it is
-- garbage collected.
--
-- Use query to perform a search or findMessage to
-- search for a particular message.
--
-- The Database type carries a phantom for the database mode,
-- which is propgated to derived Query, Thread and
-- Message objects. This is used to prevent write operations being
-- performed against a read-only database.
data Database (a :: DatabaseMode)
-- | This is an internal class whose instances are the promoted
-- DatabaseMode constructors.
class Mode a
data DatabaseMode
DatabaseModeReadOnly :: DatabaseMode
DatabaseModeReadWrite :: DatabaseMode
-- | Convenience synonym for the promoted DatabaseModeReadOnly
-- constructor.
type RO = 'DatabaseModeReadOnly
-- | Convenience synonym for the promoted DatabaseModeReadWrite
-- constructor.
type RW = 'DatabaseModeReadWrite
-- | Query object. Cleaned up when garbage collected.
--
-- Use messages or threads to get the results.
--
-- The Query type carries a phantom for the database mode, so
-- that write operations on messages derived from it are restricted to
-- read/write database sessions.
data Query (a :: DatabaseMode)
-- | Query the database. To retrieve results from a Query, use
-- threads or messages.
query :: MonadIO m => Database a -> SearchTerm -> m (Query a)
-- | Count the number of messages matching a query.
--
-- Complexity: same as the underlying Xapian search…
queryCountMessages :: (AsNotmuchError e, MonadError e m, MonadIO m) => Query a -> m Int
-- | Count the number of threads matching a query.
--
-- Θ(n) in the number of messages!
queryCountThreads :: (AsNotmuchError e, MonadError e m, MonadIO m) => Query a -> m Int
-- | Search expression. Use Bare if you want to use a query string
-- as-is (see also notmuch-search-terms(7)).
--
-- Use show to stringify a SearchTerm.
data SearchTerm
FreeForm :: String -> SearchTerm
From :: String -> SearchTerm
To :: String -> SearchTerm
Subject :: String -> SearchTerm
Attachment :: String -> SearchTerm
Tag :: Tag -> SearchTerm
Id :: MessageId -> SearchTerm
Thread :: ThreadId -> SearchTerm
Folder :: String -> SearchTerm
Path :: String -> SearchTerm
Date :: String -> String -> SearchTerm
Asterisk :: SearchTerm
And :: SearchTerm -> SearchTerm -> SearchTerm
Or :: SearchTerm -> SearchTerm -> SearchTerm
Xor :: SearchTerm -> SearchTerm -> SearchTerm
Not :: SearchTerm -> SearchTerm
Bare :: String -> SearchTerm
-- | Objects with an associated ThreadId
class HasThread a
threadId :: (HasThread a, MonadIO m) => a -> m ThreadId
-- | Thread object. Cleaned up when garbage collected.
--
-- Use messages to get the messages of a thread.
--
-- The Thread type carries a phantom for the database mode, so
-- that write operations on messages derived from it are restricted to
-- read/write database sessions.
data Thread (a :: DatabaseMode)
-- | Returns only messages in a thread which are not replies to other
-- messages in the thread.
threadToplevelMessages :: (AsNotmuchError e, MonadError e m, MonadIO m) => Thread a -> m [Message 0 a]
-- | O(1) Date of the newest message in a SearchTerm.
threadNewestDate :: MonadIO m => Thread a -> m UTCTime
-- | Returns the subject of the first message in the query results that
-- belongs to this thread.
threadSubject :: MonadIO m => Thread a -> m ByteString
-- | Return authors of a thread. These are split into:
--
--
-- - Authors of messages matching the query (accessible via
-- matchedAuthors).
-- - Authors of non-matching messages (accessible via
-- unmatchedAuthors).
--
threadAuthors :: MonadIO m => Thread a -> m ThreadAuthors
-- | O(1) count of messages in the thread.
threadTotalMessages :: MonadIO m => Thread a -> m Int
-- | Thread identifier generated and used by libnotmuch.
type ThreadId = ByteString
-- | Objects with associated threads
class HasThreads a
threads :: (HasThreads a, AsNotmuchError e, MonadError e m, MonadIO m) => a mode -> m [Thread mode]
-- | Authors belonging to messages in a query result of a thread ordered by
-- date.
data ThreadAuthors
-- | Author of a message.
type Author = Text
-- | Lens to matched authors. See also threadAuthors.
matchedAuthors :: Lens' ThreadAuthors [Author]
-- | Lens to unmatched authors. See also threadAuthors.
unmatchedAuthors :: Lens' ThreadAuthors [Author]
-- | Look for a particular message in the database.
findMessage :: (AsNotmuchError e, MonadError e m, MonadIO m) => Database a -> MessageId -> m (Maybe (Message 0 a))
-- | Objects with associated messages.
class HasMessages a
messages :: (HasMessages a, AsNotmuchError e, MonadError e m, MonadIO m) => a mode -> m [Message 0 mode]
-- | Message object. Cleaned up when garbage collected.
--
-- The Message type carries a phantom for the database mode, so
-- that write operations are restricted to read/write database sessions.
--
-- There is also a phantom type parameter for the degree of frozenness of
-- the message. Tag operations on a frozen message are atomic, only
-- becoming visible to other threads/processes after the thaw. The
-- freeze/thaw behaviour is available via withFrozenMessage.
data Message (n :: Nat) (a :: DatabaseMode)
-- | Message-Id header value.
type MessageId = ByteString
-- | Get the message ID.
messageId :: MonadIO m => Message n a -> m MessageId
-- | Get the date the message was sent.
messageDate :: MonadIO m => Message n a -> m UTCTime
-- | Get the named header as a UTF-8 encoded string. Empty string if header
-- is missing or Nothing on error.
--
-- May open a file descriptor that will not be closed until the
-- message gets garbage collected.
messageHeader :: MonadIO m => ByteString -> Message n a -> m (Maybe ByteString)
-- | Set tags for the message. Atomic.
messageSetTags :: (MonadIO m, Foldable t) => t Tag -> Message 0 RW -> m ()
-- | Add the tag to a message. If adding/removing multiple tags, use
-- messageSetTags to set the whole tag list atomically, or use
-- withFrozenMessage to avoid inconsistent states when
-- adding/removing tags.
messageAddTag :: MonadIO m => Tag -> Message n RW -> m ()
-- | Remove the tag from a message. If adding/removing multiple tags, use
-- messageSetTags to set the whole tag list atomically, or use
-- withFrozenMessage to avoid inconsistent states when
-- adding/removing tags.
messageRemoveTag :: MonadIO m => Tag -> Message n RW -> m ()
-- | Freeze the message, run the given computation and return the result.
-- The message is always thawed at the end.
--
-- Have to start with Message 0 RW due to GHC type system
-- limitation (type-level Nat is not inductive).
withFrozenMessage :: (forall n. Message n RW -> IO a) -> Message 0 RW -> IO a
-- | Get the filename of the message.
messageFilename :: MonadIO m => Message n a -> m FilePath
-- | Index a file with the default indexing options. (This binding does not
-- yet provide a way to change the indexing options.) Returns the indexed
-- message.
--
-- If message has same message ID as another message in the database, the
-- new filename will be added to the message and the existing message is
-- returned.
--
-- Possible errors include:
--
--
-- - StatusPathError if file path is not absolute or is not an
-- extension of the database path. This check is performed in this
-- binding, not in the foreign libnotmuch code.
-- - StatusFileError when file does not exist or cannot be
-- opened
-- - StatusFileNotEmail when file does not look like an
-- email
--
indexFile :: (AsNotmuchError e, MonadError e m, MonadIO m) => Database RW -> FilePath -> m (Message 0 RW)
-- | Remove a message filename. If the message has no more filenames return
-- MessageRemoved, otherwise MessagePersists.
--
-- The underlying routine (as of notmuch v0.28) returns
-- NOTMUCH_STATUS_SUCCESS even when the given path does not
-- exist, is not an internet message, or is not recorded in the database.
-- Therefore removeFile also returns MessageRemoved in
-- this scenario. This is particularly confusing when the
-- Message-Id of the given file is known, but the the file
-- itself is unknown.
removeFile :: (AsNotmuchError e, MonadError e m, MonadIO m) => Database RW -> FilePath -> m RemoveResult
-- | Result of a removeFile operation.
data RemoveResult
MessageRemoved :: RemoveResult
MessagePersists :: RemoveResult
-- | Objects with tags
class HasTags a
tags :: (HasTags a, MonadIO m) => a -> m [Tag]
-- | Message tag. Use mkTag to construct a tag. Or use
-- -XOverloadedStrings, but beware that the IsString
-- instance is non-total.
--
-- This data type avoid copying when passing tags to libnotmuch.
-- But copies do occur when reading tags from a database.
--
-- A previous experiment with interning showed no benefit. Tags are
-- typically very short so the overhead erodes any advantage.
data Tag
-- | O(n) Just a tag, or Nothing if the string is
-- too long
--
-- Use UTF-8 encoding to include non-ASCII characters in a tag.
mkTag :: ByteString -> Maybe Tag
-- | O(1)
getTag :: Tag -> ByteString
-- | The maximum tag length. Defined as NOTMUCH_TAG_MAX in
-- notmuch.h.
tagMaxLen :: Int
data Status
StatusSuccess :: Status
StatusOutOfMemory :: Status
StatusReadOnlyDatabase :: Status
StatusXapianException :: Status
StatusFileError :: Status
StatusFileNotEmail :: Status
StatusDuplicateMessageId :: Status
StatusNullPointer :: Status
StatusTagTooLong :: Status
StatusUnbalancedFreezeThaw :: Status
StatusUnbalancedAtomic :: Status
StatusUnsupportedOperation :: Status
StatusUpgradeRequired :: Status
StatusPathError :: Status
StatusIgnored :: Status
StatusIllegalArgument :: Status
StatusMalformedCryptoProtocol :: Status
StatusFailedCryptoContextCreation :: Status
StatusUnknownCryptoProtocol :: Status
StatusNoConfig :: Status
StatusNoDatabase :: Status
StatusDatabaseExists :: Status
StatusBadQuerySyntax :: Status
StatusNoMailRoot :: Status
StatusLastStatus :: Status
-- | Classy prism for injecting a libnotmuch status code.
class AsNotmuchError s
_NotmuchError :: AsNotmuchError s => Prism' s Status
-- | The version of libnotmuch that hs-notmuch was
-- built against. (The program could be running against a
-- different version.)
libnotmuchVersion :: Version
instance Control.DeepSeq.NFData Notmuch.ThreadAuthors
instance GHC.Generics.Generic Notmuch.ThreadAuthors
instance GHC.Show.Show Notmuch.ThreadAuthors
instance Notmuch.HasThread (Notmuch.Binding.Thread a)
instance Notmuch.HasThread (Notmuch.Binding.Message n a)
instance Notmuch.HasThreads Notmuch.Binding.Query
instance Notmuch.HasMessages Notmuch.Binding.Query
instance Notmuch.HasMessages Notmuch.Binding.Thread
instance Notmuch.HasMessages (Notmuch.Binding.Message n)
instance Notmuch.HasTags (Notmuch.Binding.Database a)
instance Notmuch.HasTags (Notmuch.Binding.Thread a)
instance Notmuch.HasTags (Notmuch.Binding.Message n a)