| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Notmuch
Contents
Description
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 tagmessageAddTag[path, expr, '-':tag] -> go path expr tagmessageRemoveTag_ ->die"usage: hs-notmuch-tag-set DB-DIR SEARCH-TERM +TAG|-TAG" where go path expr tag f =runExceptT(do db <-databaseOpenpathquerydb (Bareexpr) >>=messages>>= traverse_ (f (fromStringtag))databaseDestroydb ) >>= 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):
messageHeaderwill open the file to read the headers, except for theFrom,SubjectandMessage-Idheaders 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
-rtsoptsand run with+RTS -G1). This incurs the cost of scanning the entire heap on every GC run. - In an interactive program, build with
-threadedto enable parallel GC. By default, major GC will be triggered when the program is idle for a certain time. - Manually execute
performMajorGCat relevant times to ensure that older generations get cleaned up.
- databaseOpen :: (Mode a, AsNotmuchError e, MonadError e m, MonadIO m) => FilePath -> m (Database a)
- databaseOpenReadOnly :: (AsNotmuchError e, MonadError e m, MonadIO m) => FilePath -> m (Database RO)
- databaseDestroy :: (AsNotmuchError e, MonadError e m, MonadIO m) => Database a -> m ()
- databaseVersion :: MonadIO m => Database a -> m Int
- data Database (a :: DatabaseMode)
- class Mode a
- data DatabaseMode
- type RO = DatabaseModeReadOnly
- type RW = DatabaseModeReadWrite
- data Query (a :: DatabaseMode)
- query :: MonadIO m => Database a -> SearchTerm -> m (Query a)
- queryCountMessages :: (AsNotmuchError e, MonadError e m, MonadIO m) => Query a -> m Int
- queryCountThreads :: (AsNotmuchError e, MonadError e m, MonadIO m) => Query a -> m Int
- data SearchTerm
- class HasThread a where
- data Thread (a :: DatabaseMode)
- threadToplevelMessages :: (AsNotmuchError e, MonadError e m, MonadIO m) => Thread a -> m [Message 0 a]
- threadNewestDate :: MonadIO m => Thread a -> m UTCTime
- threadSubject :: MonadIO m => Thread a -> m ByteString
- threadAuthors :: MonadIO m => Thread a -> m ThreadAuthors
- threadTotalMessages :: MonadIO m => Thread a -> m Int
- type ThreadId = ByteString
- class HasThreads a where
- data ThreadAuthors
- type Author = Text
- matchedAuthors :: Lens' ThreadAuthors [Author]
- unmatchedAuthors :: Lens' ThreadAuthors [Author]
- findMessage :: (AsNotmuchError e, MonadError e m, MonadIO m) => Database a -> MessageId -> m (Maybe (Message 0 a))
- class HasMessages a where
- data Message (n :: Nat) (a :: DatabaseMode)
- type MessageId = ByteString
- messageId :: MonadIO m => Message n a -> m MessageId
- messageDate :: MonadIO m => Message n a -> m UTCTime
- messageHeader :: MonadIO m => ByteString -> Message n a -> m (Maybe ByteString)
- messageFilename :: MonadIO m => Message n a -> m FilePath
- messageSetTags :: (MonadIO m, Foldable t) => t Tag -> Message 0 RW -> m ()
- messageAddTag :: MonadIO m => Tag -> Message n RW -> m ()
- messageRemoveTag :: MonadIO m => Tag -> Message n RW -> m ()
- withFrozenMessage :: (Message 1 RW -> IO a) -> Message 0 RW -> IO a
- class HasTags a where
- data Tag
- mkTag :: ByteString -> Maybe Tag
- getTag :: Tag -> ByteString
- tagMaxLen :: Int
- data Status
- = StatusSuccess
- | StatusOutOfMemory
- | StatusReadOnlyDatabase
- | StatusXapianException
- | StatusFileError
- | StatusFileNotEmail
- | StatusDuplicateMessageId
- | StatusNullPointer
- | StatusTagTooLong
- | StatusUnbalancedFreezeThaw
- | StatusUnbalancedAtomic
- | StatusUnsupportedOperation
- | StatusUpgradeRequired
- | StatusPathError
- | StatusIgnored
- | StatusIllegalArgument
- | StatusMalformedCryptoProtocol
- | StatusFailedCryptoContextCreation
- | StatusUnknownCryptoProtocol
- | StatusLastStatus
- class AsNotmuchError s where
- libnotmuchVersion :: Version
Opening a database
databaseOpen :: (Mode a, AsNotmuchError e, MonadError e m, MonadIO m) => FilePath -> m (Database a) Source #
Open a database. The mode is determined by usage.
Because read-only functions also work on read-write databases,
databaseOpenReadOnly is also provided for convenience.
Use databaseDestroy to close a database and free resources.
databaseOpenReadOnly :: (AsNotmuchError e, MonadError e m, MonadIO m) => FilePath -> m (Database RO) Source #
Convenience function for opening a database read-only
databaseDestroy :: (AsNotmuchError e, MonadError e m, MonadIO m) => Database a -> m () Source #
Close the database and free associated resources
Don't use any resources derived from this database after using this function!
databaseVersion :: MonadIO m => Database a -> m Int Source #
Database format version of the given database.
data Database (a :: DatabaseMode) Source #
A database handle. Use databaseDestroy to close a database
(take care not to use any derived objects afterwards!)
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.
Database modes
This is an internal class whose instances are the promoted
DatabaseMode constructors.
Minimal complete definition
getMode, upgrade
Instances
type RO = DatabaseModeReadOnly Source #
Convenience synonym for the promoted DatabaseModeReadOnly constructor.
type RW = DatabaseModeReadWrite Source #
Convenience synonym for the promoted DatabaseModeReadWrite constructor.
Querying the database
data Query (a :: DatabaseMode) Source #
Query object. Cleaned up when garbate 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.
Instances
| HasThreads Query Source # | Retrieve the threads matching a |
| HasMessages Query Source # | Retrieve all messages matching a |
queryCountMessages :: (AsNotmuchError e, MonadError e m, MonadIO m) => Query a -> m Int Source #
Count the number of messages matching a query.
Complexity: same as the underlying Xapian search…
queryCountThreads :: (AsNotmuchError e, MonadError e m, MonadIO m) => Query a -> m Int Source #
Count the number of threads matching a query.
Θ(n) in the number of messages!
Search expressions
data SearchTerm Source #
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.
Constructors
Instances
| Show SearchTerm Source # | Stringify a query expression. |
| Generic SearchTerm Source # | |
| NFData SearchTerm Source # | |
| type Rep SearchTerm Source # | |
Working with threads
data Thread (a :: DatabaseMode) Source #
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.
threadToplevelMessages :: (AsNotmuchError e, MonadError e m, MonadIO m) => Thread a -> m [Message 0 a] Source #
Returns only messages in a thread which are not replies to other messages in the thread.
threadNewestDate :: MonadIO m => Thread a -> m UTCTime Source #
Returns the date of the newest message in a Thread.
threadSubject :: MonadIO m => Thread a -> m ByteString Source #
Returns the subject of the first message in the query results that belongs to this thread.
threadAuthors :: MonadIO m => Thread a -> m ThreadAuthors Source #
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).
threadTotalMessages :: MonadIO m => Thread a -> m Int Source #
All messages in the database belonging to the given Thread.
Thread ID
type ThreadId = ByteString Source #
Thread identifier generated and used by libnotmuch.
class HasThreads a where Source #
Objects with associated threads
Minimal complete definition
Methods
threads :: (AsNotmuchError e, MonadError e m, MonadIO m) => a mode -> m [Thread mode] Source #
Instances
| HasThreads Query Source # | Retrieve the threads matching a |
Thread authors
data ThreadAuthors Source #
Authors belonging to messages in a query result of a thread ordered by date.
Instances
matchedAuthors :: Lens' ThreadAuthors [Author] Source #
Lens to matched authors. See also threadAuthors.
unmatchedAuthors :: Lens' ThreadAuthors [Author] Source #
Lens to unmatched authors. See also threadAuthors.
Working with messages
findMessage :: (AsNotmuchError e, MonadError e m, MonadIO m) => Database a -> MessageId -> m (Maybe (Message 0 a)) Source #
Look for a particular message in the database.
class HasMessages a where Source #
Objects with associated messages.
Minimal complete definition
Methods
messages :: (AsNotmuchError e, MonadError e m, MonadIO m) => a mode -> m [Message 0 mode] Source #
Instances
| HasMessages Query Source # | Retrieve all messages matching a |
| HasMessages Thread Source # | Retrieve the messages in a |
| HasMessages (Message n) Source # | Retrieve the replies to a |
data Message (n :: Nat) (a :: DatabaseMode) Source #
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.
type MessageId = ByteString Source #
Message-Id header value.
messageHeader :: MonadIO m => ByteString -> Message n a -> m (Maybe ByteString) Source #
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.
messageSetTags :: (MonadIO m, Foldable t) => t Tag -> Message 0 RW -> m () Source #
Set tags for the message. Atomic.
messageAddTag :: MonadIO m => Tag -> Message n RW -> m () Source #
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.
messageRemoveTag :: MonadIO m => Tag -> Message n RW -> m () Source #
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.
withFrozenMessage :: (Message 1 RW -> IO a) -> Message 0 RW -> IO a Source #
Freeze the message, run the given computation and return the result. The message is always thawed at the end. (Don't thaw the message as part of the computation!)
Have to start with Message 0 RW due to GHC type system limitation
(type-level Nat is not inductive).
Tags
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.
mkTag :: ByteString -> Maybe Tag Source #
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.
getTag :: Tag -> ByteString Source #
O(1)
Errors
Constructors
class AsNotmuchError s where Source #
Minimal complete definition
Methods
_NotmuchError :: Prism' s Status Source #
Instances
Library information
libnotmuchVersion :: Version Source #
The version of libnotmuch that hs-notmuch was built against. (The program could be running against a different version.)