From cb3ab1a1f8bb0a982f4ce7e9e371ed316a77c84c Mon Sep 17 00:00:00 2001 From: "Danilo M." Date: Sun, 2 Aug 2026 17:52:17 +0200 Subject: feat: add cross-thread value types and notmuch RAII wrappers ThreadSummary, MessageRef, and TagChange are plain-value structs that carry query results across the worker/UI thread boundary via queued signals. NmHandle wraps libnotmuch's C handles (query, threads, messages, thread, message, tags) so early returns in the query paths can't leak. --- src/nmraii.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/types.h | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 src/nmraii.h create mode 100644 src/types.h diff --git a/src/nmraii.h b/src/nmraii.h new file mode 100644 index 0000000..22ad4ae --- /dev/null +++ b/src/nmraii.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +#include + +/// Generic owner for a notmuch handle with a destroy function. +template +class NmHandle +{ +public: + NmHandle() = default; + explicit NmHandle(T *handle) : m_handle(handle) {} + + ~NmHandle() { reset(); } + + NmHandle(const NmHandle &) = delete; + NmHandle &operator=(const NmHandle &) = delete; + + NmHandle(NmHandle &&other) noexcept + : m_handle(std::exchange(other.m_handle, nullptr)) {} + + NmHandle &operator=(NmHandle &&other) noexcept + { + if (this != &other) { + reset(); + m_handle = std::exchange(other.m_handle, nullptr); + } + return *this; + } + + void reset(T *handle = nullptr) + { + if (m_handle) + Destroy(m_handle); + m_handle = handle; + } + + T *get() const { return m_handle; } + explicit operator bool() const { return m_handle != nullptr; } + +private: + T *m_handle = nullptr; +}; + +using NmQuery = NmHandle; +using NmThreads = NmHandle; +using NmMessages = NmHandle; +using NmThread = NmHandle; +using NmMessage = NmHandle; +using NmTags = NmHandle; diff --git a/src/types.h b/src/types.h new file mode 100644 index 0000000..da71e19 --- /dev/null +++ b/src/types.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include + +struct ThreadSummary +{ + QString threadId; + QString subject; + QString authors; + QDateTime date; + int totalCount = 0; + int matchedCount = 0; + QStringList tags; + + bool isUnread() const { return tags.contains(QStringLiteral("unread")); } + bool isFlagged() const { return tags.contains(QStringLiteral("flagged")); } +}; + +struct MessageRef +{ + QString messageId; + QString filePath; + QStringList tags; + + /// True when the message itself matched the user's query, as opposed to + /// being pulled in only because a sibling in its thread matched. Drives + /// whether it renders expanded or as a stub. + bool matched = true; +}; + +/// One tag mutation, kept so it can be inverted for undo. +struct TagChange +{ + QStringList messageIds; + QStringList added; + QStringList removed; + QString description; ///< Shown in the undo action's text. + + TagChange inverted() const + { + return TagChange{ messageIds, removed, added, + QStringLiteral("Undo %1").arg(description) }; + } +}; + +Q_DECLARE_METATYPE(ThreadSummary) +Q_DECLARE_METATYPE(MessageRef) +Q_DECLARE_METATYPE(TagChange) -- cgit v1.2.3