Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
8.3 kB · 227 lines
Zig
at zig-0.16
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228// SPDX-FileCopyrightText: © 2024 Jeffrey C. Ollie// SPDX-License-Identifier: GPL-3.0-or-later
//! A directory entry in the notmuch database, obtained from//! `Database.getDirectory`.//!//! Stores a directory modification time (`getMtime`/`setMtime`) so callers//! can efficiently detect new mail, and lists the database's knowledge of the//! directory's contents via `getChildFiles` and `getChildDirectories`. The//! directory object is owned by the database; `deinit` releases it sooner.const Directory = @This();
const std = @import("std");const c = @import("c");
const Error = @import("error.zig").Error;const FilenamesIterator = @import("FilenamesIterator.zig");const wrap = @import("error.zig").wrap;
/// The underlying `notmuch_directory_t` handle; only needed directly when/// calling libnotmuch C APIs these bindings do not wrap.directory: *c.notmuch_directory_t,
/// Store an mtime within the database for `Directory`.////// The `Directory` should be an object retrieved from the database/// with `Database.getDirectory` for a particular path.////// The intention is for the caller to use the mtime to allow efficient/// identification of new messages to be added to the database. The recommended/// usage is as follows:////// o Read the mtime of a directory from the filesystem////// o Call `Database.indexFile` for all mail files in the directory////// o Call `setMtime` with the mtime read from the/// filesystem.////// Then, when wanting to check for updates to the directory in the future, the/// client can call `getMtime` and know that it only needs to add files if the/// mtime of the directory and files are newer than the stored timestamp.////// Note: The `getMtime` function does not allow the caller to distinguish a/// timestamp of 0 from a non-existent timestamp. So don't store a timestamp of/// 0 unless you are comfortable with that.pub fn setMtime(self: *const Directory, mtime: i64) Error!void { try wrap(c.notmuch_directory_set_mtime(self.directory, mtime));}
test setMtime { const fixture = @import("testing.zig"); const alloc = std.testing.allocator;
var test_db = try fixture.corpusDatabase(); defer test_db.deinit(); const db = test_db.database;
const root = db.getPath() orelse return error.TestUnexpectedResult; const new_path = try std.fs.path.joinZ(alloc, &.{ root, "new" }); defer alloc.free(new_path);
const directory = try db.getDirectory(new_path) orelse return error.TestUnexpectedResult; defer directory.deinit();
// The stored mtime is read back with getMtime. try directory.setMtime(1711962000); try std.testing.expectEqual(@as(?i64, 1711962000), directory.getMtime());}
/// Get the mtime of a directory, (as previously stored with `setMtime`).////// Returns `null` if no mtime has previously been stored for this directory.pub fn getMtime(self: *const Directory) ?i64 { const mtime = c.notmuch_directory_get_mtime(self.directory); if (mtime == 0) return null; return @intCast(mtime);}
test getMtime { const fixture = @import("testing.zig"); const alloc = std.testing.allocator;
var test_db = try fixture.corpusDatabase(); defer test_db.deinit(); const db = test_db.database;
const root = db.getPath() orelse return error.TestUnexpectedResult; const new_path = try std.fs.path.joinZ(alloc, &.{ root, "new" }); defer alloc.free(new_path);
const directory = try db.getDirectory(new_path) orelse return error.TestUnexpectedResult; defer directory.deinit();
// No mtime has been stored yet. try std.testing.expectEqual(@as(?i64, null), directory.getMtime());
// The documented caveat: a stored timestamp of 0 cannot be // distinguished from a non-existent one. try directory.setMtime(0); try std.testing.expectEqual(@as(?i64, null), directory.getMtime());}
pub const GetChildFilesError = error{ /// A Xapian exception occurred. XapianException,};
/// Get a `FilenamesIterator` listing all the filenames of messages in the/// database within the given directory.////// The returned filenames will be the basename-entries only (not complete/// paths).pub fn getChildFiles(self: *const Directory) GetChildFilesError!FilenamesIterator { return .{ .filenames = c.notmuch_directory_get_child_files(self.directory) orelse return error.XapianException, };}
test getChildFiles { const fixture = @import("testing.zig"); const alloc = std.testing.allocator;
var test_db = try fixture.corpusDatabase(); defer test_db.deinit(); const db = test_db.database;
const root = db.getPath() orelse return error.TestUnexpectedResult; const new_path = try std.fs.path.joinZ(alloc, &.{ root, "new" }); defer alloc.free(new_path);
const directory = try db.getDirectory(new_path) orelse return error.TestUnexpectedResult; defer directory.deinit();
// All the indexed corpus messages were delivered into new; the // entries are // basenames, not complete paths. var files = try directory.getChildFiles(); defer files.deinit(); var count: usize = 0; while (files.next()) |name| { try std.testing.expect(std.mem.indexOfScalar(u8, name, '/') == null); count += 1; } try std.testing.expectEqual(fixture.corpus.len, count);}
pub const GetChildDirectoriesError = error{ /// A Xapian exception occurred. XapianException,};
/// Get a `FilenamesIterator` listing all the filenames of sub-directories in/// the database within the given directory.////// The returned filenames will be the basename-entries only (not complete/// paths).pub fn getChildDirectories(self: *const Directory) GetChildDirectoriesError!FilenamesIterator { return .{ .filenames = c.notmuch_directory_get_child_directories(self.directory) orelse return error.XapianException, };}
test getChildDirectories { const fixture = @import("testing.zig"); const alloc = std.testing.allocator;
var test_db = try fixture.corpusDatabase(); defer test_db.deinit(); const db = test_db.database;
const root = db.getPath() orelse return error.TestUnexpectedResult;
// The mail root knows about new, where the corpus was delivered. const mail_root = try db.getDirectory(root) orelse return error.TestUnexpectedResult; defer mail_root.deinit(); var dirs = try mail_root.getChildDirectories(); defer dirs.deinit(); var saw_new = false; while (dirs.next()) |name| { if (std.mem.eql(u8, name, "new")) saw_new = true; } try std.testing.expect(saw_new);
// new itself has no sub-directories in the database. const new_path = try std.fs.path.joinZ(alloc, &.{ root, "new" }); defer alloc.free(new_path); const new = try db.getDirectory(new_path) orelse return error.TestUnexpectedResult; defer new.deinit(); var none = try new.getChildDirectories(); defer none.deinit(); try std.testing.expectEqual(@as(?[:0]const u8, null), none.next());}
/// Delete directory document from the database, and deinitialize the/// `Directory` object. Assumes any child directories and files have been/// deleted by the caller.pub fn delete(self: *const Directory) Error!void { try wrap(c.notmuch_directory_delete(self.directory));}
test delete { const fixture = @import("testing.zig"); const alloc = std.testing.allocator;
var test_db = try fixture.corpusDatabase(); defer test_db.deinit(); const db = test_db.database;
const root = db.getPath() orelse return error.TestUnexpectedResult; const new_path = try std.fs.path.joinZ(alloc, &.{ root, "new" }); defer alloc.free(new_path);
// Store an mtime, then delete the directory document. delete also // deinitializes the object, so no deinit call follows. const directory = try db.getDirectory(new_path) orelse return error.TestUnexpectedResult; try directory.setMtime(1711962000); try directory.delete();
// The directory document is gone from the database. try std.testing.expectEqual(@as(?Directory, null), try db.getDirectory(new_path));}
/// Deinitialize a `Directory` object.pub fn deinit(self: *const Directory) void { c.notmuch_directory_destroy(self.directory);}