Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
5.6 kB · 144 lines
Zig
at zig-0.16
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145// SPDX-FileCopyrightText: © 2024 Jeffrey C. Ollie// SPDX-License-Identifier: GPL-3.0-or-later
//! Iterator over a list of messages, as returned by `Query.searchMessages`,//! `Thread.getMessages`, and `Message.getReplies`.//!//! `next` returns `Message` objects owned by the underlying query;//! `collectTags` gathers the tags of all remaining messages instead. The//! iterator is owned by the query it derived from; `deinit` releases it//! sooner.const MessagesIterator = @This();
const std = @import("std");
const c = @import("c");
const Message = @import("Message.zig");const TagsIterator = @import("TagsIterator.zig");const status = @import("enums.zig").status;
/// The underlying `notmuch_messages_t` handle, or `null` for an iterator/// over an empty list.messages: ?*c.notmuch_messages_t,
pub const NextError = error{ /// Iteration failed to allocate memory. OutOfMemory, /// Iteration was invalidated by the database. Re-open the database and /// try again. OperationInvalidated, /// The item could not be built out of the database. /// /// `notmuch_messages_get` is documented to return NULL only for an /// out-of-memory situation, but the message is constructed on demand out /// of the database when it is asked for, so *any* failure to construct /// one arrives the same way -- a read invalidated by a writer committing /// underneath the search is how this was first seen, on a live archive /// taking mail while somebody read it. /// /// Reported as a Xapian exception rather than as `OutOfMemory`, which is /// what the header's wording would suggest, because the allocator is /// rarely the real cause and a caller that treats running out of memory /// as fatal would give up on something it should have retried. It is the /// same mapping `Directory` uses for a NULL return. XapianException, /// libnotmuch returned an undocumented status code. Unexpected,};
/// Return a list of tags from all messages.////// The resulting list is guaranteed not to contain duplicated tags.////// WARNING: You can no longer iterate over messages after calling this/// function, because the iterator will point at the end of the list. We do/// not have a function to reset the iterator yet and the only way how you can/// iterate over the list again is to recreate the message list.////// The function returns `null` on error.pub fn collectTags(self: *const MessagesIterator) ?TagsIterator { const messages = self.messages orelse return null; return .{ .tags = c.notmuch_messages_collect_tags(messages) orelse return null, };}
test collectTags { const fixture = @import("testing.zig");
var test_db = try fixture.corpusDatabase(); defer test_db.deinit(); const db = test_db.database;
const one = try db.findMessage("one@example.org") orelse return error.TestUnexpectedResult; defer one.deinit(); try one.addTag("red"); const two = try db.findMessage("two@example.org") orelse return error.TestUnexpectedResult; defer two.deinit(); try two.addTag("blue");
const query = try db.queryCreate("subject:lunch"); defer query.deinit(); var messages = try query.searchMessages(); defer messages.deinit();
// The tags of all remaining messages, gathered in one pass. This // exhausts the message iterator, per the warning above. var collected = messages.collectTags() orelse return error.TestUnexpectedResult; defer collected.deinit(); var saw_red = false; var saw_blue = false; while (collected.next()) |tag| { if (std.mem.eql(u8, tag, "red")) saw_red = true; if (std.mem.eql(u8, tag, "blue")) saw_blue = true; } try std.testing.expect(saw_red); try std.testing.expect(saw_blue); try std.testing.expect(try messages.next() == null);}
pub fn next(self: *const MessagesIterator) NextError!?Message { const messages = self.messages orelse return null; return switch (status(c.notmuch_messages_status(messages))) { .success => message: { if (c.notmuch_messages_valid(messages) == 0) break :message null; defer c.notmuch_messages_move_to_next(messages); break :message .{ .message = c.notmuch_messages_get(messages) orelse return error.XapianException, }; }, .iterator_exhausted => return null, .operation_invalidated => error.OperationInvalidated, .out_of_memory => error.OutOfMemory, else => error.Unexpected, };}
test next { const fixture = @import("testing.zig");
var test_db = try fixture.corpusDatabase(); defer test_db.deinit();
const query = try test_db.database.queryCreate("*"); defer query.deinit(); var messages = try query.searchMessages(); defer messages.deinit();
// Iterate all matches; after exhaustion, next keeps returning null. var count: usize = 0; while (try messages.next()) |message| : (count += 1) message.deinit(); try std.testing.expectEqual(fixture.corpus.len, count); try std.testing.expect(try messages.next() == null);}
/// Deinitialize a `MessagesIterator` object.////// It's not strictly necessary to call this function. All memory from/// the `MessagesIterator` object will be reclaimed when the/// containing query object is deinitialized.pub fn deinit(self: *const MessagesIterator) void { const messages = self.messages orelse return; c.notmuch_messages_destroy(messages);}