aboutsummaryrefslogtreecommitdiffstats
path: root/src/mailsync.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/mailsync.h')
-rw-r--r--src/mailsync.h80
1 files changed, 80 insertions, 0 deletions
diff --git a/src/mailsync.h b/src/mailsync.h
index a826f43..d614dbc 100644
--- a/src/mailsync.h
+++ b/src/mailsync.h
@@ -18,6 +18,7 @@
#pragma once
+#include <QDateTime>
#include <QObject>
#include <QProcess>
#include <QString>
@@ -76,6 +77,64 @@ enum class SyncOutcome {
Failed,
};
+/// What a finished run was, from the status file (item 174).
+///
+/// Three states rather than SyncOutcome's two, and the third is the point:
+/// a run that SKIPPED because another held the lock is neither a success nor a
+/// failure, and having no way to say so is why item 125 left the spinner
+/// running for ever.
+enum class SyncState {
+ Unknown,
+ Ok,
+ Failed,
+ Skipped,
+};
+
+/// One finished run of the sync script, as the script itself reported it.
+///
+/// This exists because the application used to INFER a finished run, from an
+/// inode in /proc/locks and from grepping the log for its RUN END banner. That
+/// made a human-readable line into wire format, and it could not answer the
+/// question the pending count actually needs answered: which channels did this
+/// run carry? The local sync path has always narrowed its clear to the accounts
+/// it carried; the external path could not, and cleared everything.
+///
+/// Written by assets/mailsync.sh, which is the only producer. The two agree by
+/// TEST rather than by shared code, exactly as the two readers of rules.json
+/// do: assets/test_mailsync.py pins the writer, test_mailsync.cpp pins the
+/// reader, and one test runs the real script and reads what it wrote.
+struct SyncStatus
+{
+ SyncState state = SyncState::Unknown;
+
+ /// The channels the run synced. Empty when `everyChannel` is true.
+ QStringList channels;
+
+ /// The run covered every account, which the script reports as "-a".
+ ///
+ /// Carried as a flag rather than left as the literal string in `channels`,
+ /// because "-a" is not a channel name: a caller matching it against
+ /// configured channels finds nothing and clears nothing, on exactly the run
+ /// that carried everything.
+ bool everyChannel = false;
+
+ /// Reported separately as well as folded into `state`, because they mean
+ /// different things: a failed mbsync means the edits never reached the
+ /// server, while a failed notmuch means they did and only the local index
+ /// is behind. -1 for a run where neither program ran.
+ int mbsyncStatus = -1;
+ int notmuchStatus = -1;
+
+ QDateTime started;
+ QDateTime ended;
+
+ /// True only for a run that completed with both programs succeeding.
+ /// Nothing else may clear the pending count, per the rule the local path
+ /// states: clearing on a failure asserts the edits reached the mail store
+ /// when the sync is exactly what failed to put them there.
+ bool carriedEdits() const { return state == SyncState::Ok; }
+};
+
/// Runs the configured external sync command.
///
/// qtmaildir deliberately does not implement sync itself. The existing script
@@ -125,6 +184,27 @@ public:
/// Anything unreadable, absent or unmarked is Unknown.
static SyncOutcome lastRunOutcome(const QString &logPath);
+ /// Reads the status file assets/mailsync.sh writes (item 174).
+ ///
+ /// Preferred over lastRunOutcome(), which stays as the fallback for a file
+ /// that is missing or unreadable: that is what a first run after upgrading
+ /// looks like, and deleting a working mechanism in the same change that
+ /// adds its replacement leaves two broken things instead of one.
+ ///
+ /// Anything unreadable, absent, malformed or of an unrecognised version
+ /// returns a default SyncStatus, whose state is Unknown. Callers must
+ /// change no state on Unknown, exactly as they must for SyncOutcome and
+ /// SyncMonitor::State: it is the absence of evidence, not evidence of
+ /// absence.
+ ///
+ /// A whole-file read rather than a tail, unlike lastRunOutcome(): the file
+ /// holds one run and is a few hundred bytes, where the log holds every run
+ /// of the day.
+ static SyncStatus readStatus(const QString &statusPath);
+
+ /// Where the status file lives when the config names none.
+ static QString defaultStatusPath();
+
signals:
void started();
void outputReceived(const QString &chunk);