diff options
| author | Danilo M. <danix@danix.xyz> | 2026-08-11 19:02:59 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-08-11 19:02:59 +0200 |
| commit | 72812c0ac053a1a841f3864c9e4cce5cb6dab04a (patch) | |
| tree | 26285a20ab686b1f4bb1e51e0dfbfa16e4307d1b /src/config.cpp | |
| parent | ddce3f3b198b733bd1815cd11c083f0d5ae0beb7 (diff) | |
| download | qtmaildir-72812c0ac053a1a841f3864c9e4cce5cb6dab04a.tar.gz qtmaildir-72812c0ac053a1a841f3864c9e4cce5cb6dab04a.zip | |
feat(placeholder): count sent mail and drafts on the blank pane
Item 67. The pane counted unread, flagged and inbox from three fixed
tag: queries. Sent and drafts cannot join that list as tags: tag:draft
counts 0 against a real database and no draft-ish tag exists in it at
all, so a tag-based line would be a permanent zero that reads as working
code. Both are composed from each account's folder keys instead, the
same way the Sent view already composes its query.
The drafts key was parsed and documented as unused in v1. Composing
drafts is still v2; counting them is not, so Account::draftsQuery() and
Config::allDraftsQuery() now mirror the sent pair. The shared body moved
into folderQuery() and joinAccountQueries(), so the load-bearing quoting
(a provider nests both folders under a bracketed parent, and [ and ] are
Xapian syntax) and the bare-"or" guard exist once rather than once per
folder type.
The fixed array is gone rather than extended. It held queries and labels
in two lists indexed in parallel, which is a hazard that grows with the
list: an entry inserted in one and not the other prints a real number
against the wrong name and looks entirely plausible. placeholderLines()
carries each query beside the callable that labels it, so the two cannot
drift, and the count reply stays paired by position as the worker
requires.
A line is omitted when no account configures that folder rather than
shown as 0, following item 63: a missing folder is a real configuration,
and "0 sent" claims the user has sent nothing.
Measured against the real config: 4 sent terms over 601 threads, 5
drafts terms over 3, the extra drafts term coming from the one account
that configures drafts and no sent, which is what proves the two are
collected independently.
Four tests here and four in test_config, mutation-checked at three
points: dropping the drafts line, an off-by-one in the label pairing,
and removing the -1 guard for an uncountable query. Each mutation fails
a test.
Diffstat (limited to 'src/config.cpp')
| -rw-r--r-- | src/config.cpp | 68 |
1 files changed, 49 insertions, 19 deletions
diff --git a/src/config.cpp b/src/config.cpp index 47fadec..a81651f 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -47,37 +47,67 @@ QString Account::scopedQuery(const QString &query) const return QStringLiteral("%1 and (%2)").arg(prefix, query); } -QString Account::sentQuery() const +namespace { + +/// Composes `path:"<maildir>/<folder>/**"`, or empty when the folder is unset. +/// +/// The QUOTES are load-bearing, not decoration. A real provider nests both its +/// sent and its drafts folder under a bracketed parent with a localised name, +/// "[Provider]/Posta inviata" and "[Provider]/Bozze", and "[" and "]" are +/// Xapian syntax: unquoted, the term is parsed rather than matched and the +/// query silently returns nothing while looking correct. +/// +/// The path is user config and is interpolated into a query, so this is the +/// only place that composition happens; a caller building it by hand would be +/// a second chance to forget the quotes. +QString folderQuery(const QString &maildir, const QString &folder) { - if (sent.isEmpty()) + if (folder.isEmpty()) return QString(); - - // The QUOTES are load-bearing, not decoration. A real provider nests its - // sent folder under a bracketed parent, "[Provider]/Posta inviata", and - // "[" and "]" are Xapian syntax: unquoted, the term is parsed rather than - // matched and the query silently returns nothing while looking correct. - // - // The path is user config and is interpolated into a query, so this is the - // only place that composition happens; a caller building it by hand would - // be a second chance to forget the quotes. - return QStringLiteral("path:\"%1/%2/**\"").arg(maildir, sent); + return QStringLiteral("path:\"%1/%2/**\"").arg(maildir, folder); } -QString Config::allSentQuery() const +/// Joins the non-empty results of `extract` across `accounts` with " or ". +/// +/// Collect first, join after. Appending "or" per account and trimming the +/// result is the version that produced the defect this guards: an account with +/// no key contributes an empty term, notmuch accepts the bare "or" without +/// complaint, and the query quietly means something else. Measured against a +/// real database, `A or or B` returns 190 where the correct pair returns 211. +QString joinAccountQueries(const QList<Account> &accounts, + QString (Account::*extract)() const) { - // Collect first, join after. Appending "or" per account and trimming the - // result is the version that produced the defect this guards: an account - // with no sent key contributes an empty term, notmuch accepts the bare - // "or" without complaint, and the query quietly means something else. QStringList parts; - for (const Account &account : m_accounts) { - const QString query = account.sentQuery(); + for (const Account &account : accounts) { + const QString query = (account.*extract)(); if (!query.isEmpty()) parts.append(query); } return parts.join(QStringLiteral(" or ")); } +} // namespace + +QString Account::sentQuery() const +{ + return folderQuery(maildir, sent); +} + +QString Account::draftsQuery() const +{ + return folderQuery(maildir, drafts); +} + +QString Config::allSentQuery() const +{ + return joinAccountQueries(m_accounts, &Account::sentQuery); +} + +QString Config::allDraftsQuery() const +{ + return joinAccountQueries(m_accounts, &Account::draftsQuery); +} + QString Config::defaultPath() { const QString base = |
