From 75ce7822a7fee444c31a4709e19ad4be16ca5877 Mon Sep 17 00:00:00 2001 From: "Jeffrey C. Ollie" Date: Sun, 1 Mar 2026 17:50:22 -0600 Subject: [PATCH] more documentation updates --- src/Database.zig | 67 +++++++++++++++++++++++++++++++++++++++++++++++- src/enums.zig | 11 ++++++-- src/error.zig | 60 ++++++++++++++++++++++++++++++++++++++----- 3 files changed, 129 insertions(+), 9 deletions(-) diff --git a/src/Database.zig b/src/Database.zig index b9c87d1..6ac5875 100644 --- a/src/Database.zig +++ b/src/Database.zig @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: © 2024 Jeffrey C. Ollie -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-or-later const Database = @This(); @@ -17,6 +17,8 @@ const CONFIG = enums.CONFIG; const DATABASE_MODE = enums.DATABASE_MODE; const DECRYPT = enums.DECRYPT; const QUERY_SYNTAX = enums.QUERY_SYNTAX; +const STATUS = enums.STATUS; +const status = enums.status; const Message = @import("Message.zig"); const Query = @import("Query.zig"); @@ -164,6 +166,69 @@ pub fn upgrade(self: *const Database, progress_notify: ?UpgradeProgressNotifyCal try wrap(c.notmuch_database_upgrade(self.database, progress_notify, closure)); } +/// Begin an atomic database operation. +/// +/// Any modifications performed between a successful begin and a +/// notmuch_database_end_atomic will be applied to the database atomically. +/// Note that, unlike a typical database transaction, this only ensures +/// atomicity, not durability; neither begin nor end necessarily flush +/// modifications to disk. +/// +/// Atomic sections may be nested. begin_atomic and end_atomic must always be +/// called in pairs. +pub fn beginAtomic(self: *const Database) error{XapianException}!void { + switch (status(c.notmuch_database_begin_atomic(self.database))) { + .SUCCESS => {}, + .XAPIAN_EXCEPTION => return error.XapianException, + else => unreachable, + } +} + +/// Indicate the end of an atomic database operation. If repeated (with matching +/// notmuch_database_begin_atomic) "database.autocommit" times, commit the the +/// transaction and all previous (non-cancelled) transactions to the database. +pub fn endAtomic(self: *const Database) error{ UnbalancedAtomic, XapianException }!void { + switch (status(c.notmuch_database_begin_atomic(self.database))) { + .SUCCESS => {}, + .UNBALANCED_ATOMIC => return error.UnbalancedAtomic, + .XAPIAN_EXCEPTION => return error.XapianException, + else => unreachable, + } +} + +pub const Revision = struct { + revision: u64, + uuid: []const u8, + + pub fn compare(self: Revision, other: Revision) error{DatabaseMismatch}!enum { lt, eq, gt } { + if (!std.mem.eql(u8, self.uuid, other.uuid)) return error.DatabaseMismatch; + if (self.revision < other.revision) return .lt; + if (self.revision > other.revision) return .gt; + return .eq; + } +}; + +/// Return the committed database revision and UUID. +/// +/// The database revision number increases monotonically with each commit to the +/// database. Hence, all messages and message changes committed to the database +/// (that is, visible to readers) have a last modification revision <= the +/// committed database revision. Any messages committed in the future will be +/// assigned a modification revision > the committed database revision. +/// +/// The UUID is a opaque string that uniquely identifies this database. Two +/// revision numbers are only comparable if they have the same database UUID. +/// The string 'uuid' is owned by notmuch and should not be freed or modified by +/// the user. +pub fn getRevision(self: *const Database) Revision { + var uuid: [*c]const u8 = undefined; + const revision = c.notmuch_database_get_revision(self.database, &uuid); + return .{ + .revision = revision, + .uuid = std.mem.span(uuid), + }; +} + pub fn indexFileGetMessage(self: *const Database, filename: [:0]const u8, indexopts: ?IndexOpts) Error!Message { var message: ?*c.notmuch_message_t = null; wrap(c.notmuch_database_index_file( diff --git a/src/enums.zig b/src/enums.zig index 45a3a88..bc6ad7b 100644 --- a/src/enums.zig +++ b/src/enums.zig @@ -5,6 +5,8 @@ const std = @import("std"); const c = @import("c"); +/// Generate Zig enums from C enums that begin with a prefix. Skip creating +/// enums for certain names. fn generateEnum(comptime prefix: []const u8, skips: []const []const u8) type { @setEvalBranchQuota(24000); const info = @typeInfo(c); @@ -18,14 +20,14 @@ fn generateEnum(comptime prefix: []const u8, skips: []const []const u8) type { } } const TagType = std.math.IntFittingRange(0, max); - var field_names: [count]std.builtin.Type.EnumField = undefined; + var field_names: [count][]const u8 = undefined; var field_values: [count]TagType = undefined; var index = 0; outer: for (info.@"struct".decls) |decl| { for (skips) |skip| if (std.mem.eql(u8, skip, decl.name)) continue :outer; if (std.mem.cutPrefix(u8, decl.name, prefix)) |suffix| { field_names[index] = suffix; - field_values = @field(c, decl.name); + field_values[index] = @field(c, decl.name); index += 1; } } @@ -57,3 +59,8 @@ pub const SORT = generateEnum("NOTMUCH_SORT_", &.{}); /// Status codes used for the return values of most functions. pub const STATUS = generateEnum("NOTMUCH_STATUS_", &.{"NOTMUCH_STATUS_LAST_STATUS"}); + +/// Convenience function to convert a notmuch API return code to a STATUS enum. +pub fn status(rc: c_uint) STATUS { + return @enumFromInt(rc); +} diff --git a/src/error.zig b/src/error.zig index 7925251..7778e25 100644 --- a/src/error.zig +++ b/src/error.zig @@ -1,5 +1,5 @@ // SPDX-FileCopyrightText: © 2024 Jeffrey C. Ollie -// SPDX-License-Identifier: MIT +// SPDX-License-Identifier: GPL-3.0-or-later const std = @import("std"); @@ -8,39 +8,80 @@ const log = std.log.scoped(.notmuch); const c = @import("c"); const STATUS = @import("enums.zig").STATUS; +const status = @import("enums.zig").status; pub const Error = error{ + /// Syntax error in query. BadQuerySyntax, + /// Database is not fully opened, or has been closed. ClosedDatabase, - /// Database already exists, not created. + /// Database already exists, so not (re-)created. DatabaseExists, + /// A file contains a message ID that is identical to a message already in + /// the database. DuplicateMessageID, + /// Notmuch attempted to do crypto processing, but could not initialize the + /// engine needed to do so. FailedCryptoContextCreation, - /// An error occurred trying to open the database or config file (such as - /// permission denied, or file not found, etc.). + /// An error occurred trying to read or write to a file (this could be file + /// not found, permission denied, etc.) FileError, + /// A file was presented that doesn't appear to be an email message. FileNotEmail, /// There was an error determining the database format version. FormatVersionError, + /// The requested operation was ignored. Depending on the function, this may + /// not be an actual error. Ignored, + /// One of the arguments violates the preconditions for the function, in a + /// way not covered by a more specific argument. IllegalArgument, + /// The iterator being examined has been exhausted and contains no more + /// items. + IteratorExhausted, + /// A MIME object claimed to have cryptographic protection which notmuch + /// tried to handle, but the protocol was not specified in an intelligible + /// way. MaformedCryptoProtocol, + /// Unable to load a config file. NoConfig, + /// Unable to load a database. NoDatabase, + /// No mail root could be deduced from parameters and environment. NoMailRoot, /// A newer version of the notmuch library is required. NotmuchVersion, + /// The user erroneously passed a NULL pointer to a notmuch function. NullPointer, + /// An operation that was being performed on the database has been + /// invalidated while in progress, and must be re-executed. + /// + /// This will typically happen while iterating over query results and the + /// underlying Xapian database is modified by another process so that the + /// currently open version cannot be read anymore. + OperationInvalidated, /// Out of memory. OutOfMemory, + /// There is a problem with the proposed path, e.g. a relative path passed + /// to a function expecting an absolute path. PathError, + /// An attempt was made to write to a database opened in read-only mode. ReadOnlyDatabase, + /// A tag value is too long (exceeds NOTMUCH_TAG_MAX). TagTooLong, + /// notmuch_database_end_atomic has been called more times than + /// notmuch_database_begin_atomic. UnbalancedAtomic, + /// The notmuch_message_thaw function has been called more times than + /// notmuch_message_freeze. UnbalancedFreezeThaw, + /// A MIME object claimed to have cryptographic protection, and notmuch + /// attempted to process it, but the specific protocol was something that + /// notmuch doesn't know how to handle. UnknownCryptoProtocol, + /// The operation is not supported. UnsupportedOperation, - /// The database needs to be upgraded to a newer format. + /// The operation requires a database upgrade. UpgradeRequired, /// A Xapian exception occurred. XapianException, @@ -55,7 +96,7 @@ pub fn wrapMessage(rc: c.notmuch_status_t, message: [*c]const u8) Error!void { } pub fn wrap(rc: c.notmuch_status_t) Error!void { - return switch (@as(STATUS, @enumFromInt(rc))) { + return switch (status(rc)) { .SUCCESS => {}, .BAD_QUERY_SYNTAX => error.BadQuerySyntax, .CLOSED_DATABASE => error.ClosedDatabase, @@ -66,11 +107,13 @@ pub fn wrap(rc: c.notmuch_status_t) Error!void { .FILE_NOT_EMAIL => error.FileNotEmail, .IGNORED => error.Ignored, .ILLEGAL_ARGUMENT => error.IllegalArgument, + .ITERATOR_EXHAUSTED => error.IteratorExhausted, .MALFORMED_CRYPTO_PROTOCOL => error.MaformedCryptoProtocol, .NO_CONFIG => error.NoConfig, .NO_DATABASE => error.NoDatabase, .NO_MAIL_ROOT => error.NoMailRoot, .NULL_POINTER => error.NullPointer, + .OPERATION_INVALIDATED => error.OperationInvalidated, .OUT_OF_MEMORY => error.OutOfMemory, .PATH_ERROR => error.PathError, .READ_ONLY_DATABASE => error.ReadOnlyDatabase, @@ -83,3 +126,8 @@ pub fn wrap(rc: c.notmuch_status_t) Error!void { .XAPIAN_EXCEPTION => error.XapianException, }; } + +test wrap { + try wrap(c.NOTMUCH_STATUS_SUCCESS); + try std.testing.expectError(error.BadQuerySyntax, wrap(c.NOTMUCH_STATUS_BAD_QUERY_SYNTAX)); +} -- 2.51.2