data/MailSnapshotReader.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #pragma once | ||
| 2 | |||
| 3 | #include <QByteArray> | ||
| 4 | #include <QHash> | ||
| 5 | #include <QList> | ||
| 6 | #include <QPair> | ||
| 7 | #include <QSet> | ||
| 8 | #include <QSqlDatabase> | ||
| 9 | #include <QSqlQuery> | ||
| 10 | #include <QString> | ||
| 11 | |||
| 12 | #include <atomic> | ||
| 13 | |||
| 14 | #include "data/MailDisplaySnapshot.h" | ||
| 15 | #include "data/MailSelectionSnapshot.h" | ||
| 16 | #include "data/Models.h" | ||
| 17 | |||
| 18 | struct sqlite3; | ||
| 19 | |||
| 20 | // Read-only worker snapshots over a MailCache connection (Sprint 92 | ||
| 21 | // S92.4.1, extracted from MailCache): the display snapshot of one mail, | ||
| 22 | // the selection snapshot of the visual selection, the flag comparison of a | ||
| 23 | // server snapshot with the visible model and cached membership, and the | ||
| 24 | // bounded reply/forward compose source. Callers reach it as | ||
| 25 | // `cache.snapshots()`. | ||
| 26 | // | ||
| 27 | // Plain class, no connection of its own (Sprint 91 rule 6): it reads | ||
| 28 | // through the owning MailCache's QSqlDatabase on whichever thread owns that | ||
| 29 | // cache instance — the read services open their own read-only MailCache. | ||
| 30 | // It never writes; selectionSnapshot and composeSourceSnapshot open and roll | ||
| 31 | // back their own read transaction. The header page snapshot stays in | ||
| 32 | // MailCache because it builds on the cache's paging and count functions. | ||
| 33 | class MailSnapshotReader { | ||
| 34 | public: | ||
| 35 | 5035 | explicit MailSnapshotReader(QSqlDatabase &db) : m_db(db) {} | |
| 36 | |||
| 37 | MailDisplaySnapshot displaySnapshot(const QString &accountId, | ||
| 38 | qint64 folderId, qint64 uid, | ||
| 39 | MailDisplaySnapshot::PreferredContent preferred = | ||
| 40 | MailDisplaySnapshot::PreferredContent::Auto, | ||
| 41 | const std::atomic_bool *cancelled = nullptr); | ||
| 42 | MailSelectionResult selectionSnapshot(const MailSelectionQuery &query, | ||
| 43 | const std::atomic_bool &cancelled); | ||
| 44 | |||
| 45 | struct FlagSnapshotDiff { | ||
| 46 | QList<qint64> missingUids; | ||
| 47 | QList<QPair<qint64, quint32>> visibleChanged; | ||
| 48 | QList<qint64> visibleExpunged; | ||
| 49 | // Cached headers outside the visible snapshot: server flags that differ | ||
| 50 | // (pending local intents excluded) and UIDs the server no longer has. | ||
| 51 | QList<QPair<qint64, quint32>> offPageChanged; | ||
| 52 | QList<qint64> offPageExpunged; | ||
| 53 | QString error; | ||
| 54 | bool stale = false; | ||
| 55 | bool cancelled = false; | ||
| 56 | }; | ||
| 57 | // Worker-owned read-only connection: compare the server snapshot with the | ||
| 58 | // visible model snapshot and all cached UID membership off the GUI thread. | ||
| 59 | // `membership` (CONDSTORE reopen) lists every server UID while | ||
| 60 | // `serverFlags` then holds only the messages changed since the last | ||
| 61 | // modseq; without it `serverFlags` is the complete snapshot. | ||
| 62 | FlagSnapshotDiff compareFlagSnapshot(const QString &account, | ||
| 63 | const QString &folder, qint64 folderId, quint32 uidValidity, | ||
| 64 | const QList<QPair<qint64, quint32>> &serverFlags, | ||
| 65 | const QHash<qint64, quint32> &visibleFlags, | ||
| 66 | const QSet<qint64> &pendingFlagUids, | ||
| 67 | const std::atomic_bool &cancelled, | ||
| 68 | const QList<qint64> *membership = nullptr) const; | ||
| 69 | struct ComposeSourceSnapshot { | ||
| 70 | enum class Outcome { Ready, Missing, Limited, Failed }; | ||
| 71 | Outcome outcome = Outcome::Failed; | ||
| 72 | MailHeader header; | ||
| 73 | QString quoteText; | ||
| 74 | // Sprint 108 D-9/D-21: the original's HTML (≤ 1 MiB, the import bound); | ||
| 75 | // empty for plain-text mails or when it is larger. | ||
| 76 | QString quoteHtml; | ||
| 77 | QByteArray envelopePrefix; | ||
| 78 | quint32 uidValidity = 0; | ||
| 79 | bool bodyPresent = false; | ||
| 80 | bool quoteTruncated = false; | ||
| 81 | QString error; | ||
| 82 | }; | ||
| 83 | // Bounded reply/forward source, read in one account-fenced transaction. | ||
| 84 | // The worker can abandon a superseded Compose intent during SQLite work. | ||
| 85 | ComposeSourceSnapshot composeSourceSnapshot(const QString &accountId, | ||
| 86 | qint64 folderId, qint64 uid, quint32 uidValidity, | ||
| 87 | const std::atomic_bool &cancelled); | ||
| 88 | |||
| 89 | private: | ||
| 90 | // displaySnapshot() steps (S91.3.9): pure row/SQL helpers, no rollback. | ||
| 91 | static QString installDisplayCancellation(const QSqlDatabase &db, | ||
| 92 | const std::atomic_bool *cancelled, | ||
| 93 | sqlite3 **database); | ||
| 94 | static QString displayRowQuery(); | ||
| 95 | static void readDisplayHeader(const QSqlQuery &query, qint64 uid, qint64 folderId, | ||
| 96 | MailHeader &header); | ||
| 97 | static QString displayBodyProjection(MailDisplaySnapshot::PreferredContent preferred, | ||
| 98 | qint64 plainBytes, qint64 htmlBytes, | ||
| 99 | MailBody::Projection &bodyProjection); | ||
| 100 | static Attachment readDisplayAttachment(const QSqlQuery &query); | ||
| 101 | static void readDisplayBody(const QSqlQuery &query, MailBody &body); | ||
| 102 | |||
| 103 | QSqlDatabase &m_db; // owned by MailCache | ||
| 104 | }; | ||
| 105 |