Zig bindings for the notmuch C library
0

Configure Feed

Select the types of activity you want to include in your feed.

add more APIs, documentation

Jeffrey C. Ollie (Mar 1, 2026, 5:01 PM -0600) 96e839b7 66271e9f

+118 -33
+94 -26
src/Database.zig
··· 24 24 database: *c.notmuch_database_t, 25 25 26 26 pub const OpenOptions = struct { 27 - config_path: ?[:0]const u8, 28 - database_path: ?[*:0]const u8, 29 - profile: ?[:0]const u8, 27 + /// Specify a config file. 28 + config_path: ?[:0]const u8 = null, 29 + /// Specify a database path. 30 + database_path: ?[*:0]const u8 = null, 31 + /// Specify a profile. 32 + profile: ?[:0]const u8 = null, 30 33 }; 31 34 35 + /// Open an existing notmuch database. 32 36 pub fn open(mode: DATABASE_MODE, options: OpenOptions) Error!Database { 33 37 if (!c.LIBNOTMUCH_CHECK_VERSION(5, 6, 0)) { 34 38 return error.NotmuchVersion; ··· 37 41 var error_message: [*c]u8 = null; 38 42 var database: ?*c.notmuch_database_t = null; 39 43 try wrapMessage(c.notmuch_database_open_with_config( 40 - options.database_path orelse null, 44 + options.database_path, 41 45 @intFromEnum(mode), 42 - options.config_path orelse null, 43 - options.profile orelse null, 46 + options.config_path, 47 + options.profile, 44 48 &database, 45 49 &error_message, 46 50 ), error_message); ··· 50 54 } 51 55 52 56 pub const CreateOptions = struct { 53 - config_path: ?[:0]const u8, 54 - database_path: ?[*:0]const u8, 55 - profile: ?[:0]const u8, 57 + /// Specify a config file. 58 + config_path: ?[:0]const u8 = null, 59 + /// Specify a database path. 60 + database_path: ?[*:0]const u8 = null, 61 + /// Specify a profile. 62 + profile: ?[:0]const u8 = null, 56 63 }; 57 64 65 + /// Create a new notmuch database. 58 66 pub fn create(options: CreateOptions) Error!Database { 59 67 if (!c.LIBNOTMUCH_CHECK_VERSION(5, 6, 0)) { 60 68 return error.NotmuchVersion; ··· 64 72 var database: ?*c.notmuch_database_t = null; 65 73 try wrapMessage( 66 74 c.notmuch_database_create_with_config( 67 - options.database_path orelse null, 68 - options.config_path orelse null, 69 - options.profile orelse null, 75 + options.database_path, 76 + options.config_path, 77 + options.profile, 70 78 &database, 71 79 &error_message, 72 80 ), ··· 77 85 }; 78 86 } 79 87 88 + /// Close the database. 80 89 pub fn close(self: *const Database) void { 81 90 _ = c.notmuch_database_close(self.database); 82 91 } 83 92 93 + /// Destroy the notmuch database, closing it if necessary and freeing all 94 + /// associated resources. 95 + /// 96 + /// Return value as in notmuch_database_close if the database was open; 97 + /// notmuch_database_destroy itself has no failure modes. 98 + pub fn destroy(self: *const Database) Error!void { 99 + try wrap(c.notmuch_database_destroy(self.database)); 100 + } 101 + 84 102 pub fn indexFile(self: *const Database, filename: [:0]const u8, indexopts: ?IndexOpts) Error!void { 85 103 try wrap(c.notmuch_database_index_file( 86 104 self.database, ··· 90 108 )); 91 109 } 92 110 111 + /// A callback invoked by Database.compact to notify the user of the 112 + /// progress of the compaction process. 113 + pub const StatusCallback = fn (message: [*c]const u8, closure: ?*anyopaque) callconv(.c) void; 114 + 115 + /// Compact a notmuch database, backing up the original database to the given 116 + /// path. 117 + /// 118 + /// The database will be opened in read-write mode during the compaction process 119 + /// to ensure no writes are made. 120 + /// 121 + /// If the optional callback function `status_cb` is non-`null`, it will be 122 + /// called with diagnostic and informational messages. The argument `closure` is 123 + /// passed verbatim to any callback invoked. 124 + pub fn compact(path: [:0]const u8, backup_path: [:0]const u8, status_cb: ?StatusCallback, closure: ?*anyopaque) Error!void { 125 + try wrap(c.notmuch_database_compact(path, backup_path, status_cb, closure)); 126 + } 127 + 128 + /// Return the database format version of the database. 129 + pub fn getVersion(self: *const Database) error{FormatVersionError}!c_uint { 130 + const version = c.notmuch_database_get_version(self.database); 131 + if (version == 0) return error.FormatVersionError; 132 + return version; 133 + } 134 + 135 + /// Can the database be upgraded to a newer database version? 136 + /// 137 + /// If this function returns TRUE, then the caller may call 138 + /// notmuch_database_upgrade to upgrade the database. If the caller does 139 + /// not upgrade an out-of-date database, then some functions may fail with 140 + /// NOTMUCH_STATUS_UPGRADE_REQUIRED. This always returns FALSE for a read-only 141 + /// database because there's no way to upgrade a read-only database. 142 + /// 143 + /// Also returns FALSE if an error occurs accessing the database. 144 + pub fn needsUpgrade(self: *const Database) bool { 145 + return c.notmuch_database_needs_upgrade(self.database) != 0; 146 + } 147 + 148 + pub const UpgradeProgressNotifyCallback = fn (closure: ?*anyopaque, progress: f64) callconv(.c) void; 149 + 150 + /// Upgrade the current database to the latest supported version. 151 + /// 152 + /// This ensures that all current notmuch functionality will be available on the 153 + /// database. After opening a database in read-write mode, it is recommended 154 + /// that clients check if an upgrade is needed (Database.needsUpgrade) and 155 + /// if so, upgrade with this function before making any modifications. If 156 + /// Database.needsUpgrade returns FALSE, this will be a no-op. 157 + /// 158 + /// The optional `progress_notify` callback can be used by the caller to provide 159 + /// progress indication to the user. If non-`null` it will be called periodically 160 + /// with `progress` as a floating-point value in the range of [0.0 .. 1.0] 161 + /// indicating the progress made so far in the upgrade process. The argument 162 + /// `closure` is passed verbatim to any callback invoked. 163 + pub fn upgrade(self: *const Database, progress_notify: ?UpgradeProgressNotifyCallback, closure: ?*anyopaque) Error!void { 164 + try wrap(c.notmuch_database_upgrade(self.database, progress_notify, closure)); 165 + } 166 + 93 167 pub fn indexFileGetMessage(self: *const Database, filename: [:0]const u8, indexopts: ?IndexOpts) Error!Message { 94 168 var message: ?*c.notmuch_message_t = null; 95 169 wrap(c.notmuch_database_index_file( ··· 134 208 return std.mem.span(config orelse return null); 135 209 } 136 210 137 - /// get a configuration value from an open database. 211 + /// Get a configuration value from an open database. 138 212 /// 139 213 /// This value reflects all configuration information given at the time 140 214 /// the database was opened. 141 215 /// 142 - /// Returns NULL if 'key' unknown or if no value is known for 'key'. 143 - /// Otherwise returns a string owned by notmuch which should not be modified 216 + /// Returns `null` if `key` is unknown or if no value is known for `key`. 217 + /// Otherwise returns a string owned by `notmuch` which should not be modified 144 218 /// nor freed by the caller. 145 219 pub fn configGet(self: *const Database, key: CONFIG) Error!?[:0]const u8 { 146 220 return std.mem.span(c.notmuch_config_get(self.database, @intFromEnum(key)) orelse return null); 147 221 } 148 222 149 - /// set a configuration value 223 + /// Set a configuration value 150 224 pub fn configSet(self: *const Database, key: CONFIG, value: [:0]const u8) Error!void { 151 225 try wrap(c.notmuch_config_set(self.database, @intFromEnum(key), value)); 152 226 } 153 227 154 - /// Returns an iterator for a ';'-delimited list of configuration values 228 + /// Returns an iterator for a `;`-delimited list of configuration values. 155 229 /// 156 230 /// These values reflect all configuration information given at the 157 231 /// time the database was opened. ··· 197 271 }; 198 272 } 199 273 200 - /// Create a new query for 'database'. 201 - /// 202 - /// Here, 'database' should be an open database, (see `open` and `create`). 274 + /// Create a new query. 203 275 /// 204 276 /// For the query string, we'll document the syntax here more completely in the 205 277 /// future, but it's likely to be a specialized version of the general Xapian ··· 215 287 /// `Query.searchMessages` and `Query.searchThreads` to actually execute the 216 288 /// query. 217 289 pub fn queryCreate(self: *const Database, query_string: [:0]const u8) Error!Query { 218 - return .{ 219 - .query = c.notmuch_query_create(self.database, query_string) orelse return error.OutOfMemory, 220 - }; 290 + return .init(c.notmuch_query_create(self.database, query_string) orelse return error.OutOfMemory); 221 291 } 222 292 223 293 pub fn queryCreateWithSyntax(self: *const Database, query_string: [:0]const u8, syntax: QUERY_SYNTAX) Error!Query { ··· 225 295 226 296 try wrap(c.notmuch_query_create_with_syntax(self.database, query_string, @intFromEnum(syntax), &query)); 227 297 228 - return .{ 229 - .query = query orelse return error.OutOfMemory, 230 - }; 298 + return .init(query orelse return error.OutOfMemory); 231 299 } 232 300 233 301 pub const IndexOpts = struct {
+9 -5
src/Query.zig
··· 18 18 19 19 query: *c.notmuch_query_t, 20 20 21 - /// Return the query_string of this query. 21 + pub fn init(query: *c.notmuch_query_t) Query { 22 + return .{ .query = query }; 23 + } 24 + 25 + /// Return the query string of this query. 22 26 pub fn getQueryString(self: *const Query) [:0]const u8 { 23 27 return std.mem.span(c.notmuch_query_get_query_string(self.query)); 24 28 } 25 29 26 - /// Specify whether to omit excluded results or simply flag them. By default, 30 + /// Specify whether to omit excluded results or simply flag them. By default, 27 31 /// this is set to TRUE. 28 32 /// 29 33 /// If set to TRUE or ALL, notmuch_query_search_messages will omit excluded 30 34 /// messages from the results, and notmuch_query_search_threads will 31 - /// omit threads that match only in excluded messages. If set to TRUE, 35 + /// omit threads that match only in excluded messages. If set to TRUE, 32 36 /// notmuch_query_search_threads will include all messages in threads that 33 - /// match in at least one non-excluded message. Otherwise, if set to ALL, 37 + /// match in at least one non-excluded message. Otherwise, if set to ALL, 34 38 /// notmuch_query_search_threads will omit excluded messages from all threads. 35 39 /// 36 40 /// If set to FALSE or FLAG then both notmuch_query_search_messages and ··· 43 47 /// completely ignored. 44 48 /// 45 49 /// The performance difference when calling notmuch_query_search_messages should 46 - /// be relatively small (and both should be very fast). However, in some cases, 50 + /// be relatively small (and both should be very fast). However, in some cases, 47 51 /// notmuch_query_search_threads is very much faster when omitting excluded 48 52 /// messages as it does not need to construct the threads that only match in 49 53 /// excluded messages.
+4 -2
src/enums.zig
··· 17 17 count += 1; 18 18 } 19 19 } 20 + const TagType = std.math.IntFittingRange(0, max); 20 21 var field_names: [count]std.builtin.Type.EnumField = undefined; 21 - var field_values: [count]std.math.IntFittingRange(0, max) = undefined; 22 + var field_values: [count]TagType = undefined; 22 23 var index = 0; 23 24 outer: for (info.@"struct".decls) |decl| { 24 25 for (skips) |skip| if (std.mem.eql(u8, skip, decl.name)) continue :outer; ··· 29 30 } 30 31 } 31 32 return @Enum( 32 - std.math.IntFittingRange(0, max), 33 + TagType, 33 34 .exhaustive, 34 35 &field_names, 35 36 &field_values, ··· 54 55 /// Sort values for notmuch_query_set_sort. 55 56 pub const SORT = generateEnum("NOTMUCH_SORT_", &.{}); 56 57 58 + /// Status codes used for the return values of most functions. 57 59 pub const STATUS = generateEnum("NOTMUCH_STATUS_", &.{"NOTMUCH_STATUS_LAST_STATUS"});
+9
src/error.zig
··· 12 12 pub const Error = error{ 13 13 BadQuerySyntax, 14 14 ClosedDatabase, 15 + /// Database already exists, not created. 15 16 DatabaseExists, 16 17 DuplicateMessageID, 17 18 FailedCryptoContextCreation, 19 + /// An error occurred trying to open the database or config file (such as 20 + /// permission denied, or file not found, etc.). 18 21 FileError, 19 22 FileNotEmail, 23 + /// There was an error determining the database format version. 24 + FormatVersionError, 20 25 Ignored, 21 26 IllegalArgument, 22 27 MaformedCryptoProtocol, 23 28 NoConfig, 24 29 NoDatabase, 25 30 NoMailRoot, 31 + /// A newer version of the notmuch library is required. 26 32 NotmuchVersion, 27 33 NullPointer, 34 + /// Out of memory. 28 35 OutOfMemory, 29 36 PathError, 30 37 ReadOnlyDatabase, ··· 33 40 UnbalancedFreezeThaw, 34 41 UnknownCryptoProtocol, 35 42 UnsupportedOperation, 43 + /// The database needs to be upgraded to a newer format. 36 44 UpgradeRequired, 45 + /// A Xapian exception occurred. 37 46 XapianException, 38 47 }; 39 48
+2
src/notmuch.zig
··· 1 1 // SPDX-FileCopyrightText: © 2024 Jeffrey C. Ollie 2 2 // SPDX-License-Identifier: MIT 3 3 4 + //! Zig bindings for the Notmuch C API. 5 + //! 4 6 const std = @import("std"); 5 7 6 8 const log = std.log.scoped(.notmuch);