using AIStudio.Tools.Databases.IndexStore; namespace AIStudio.Tools.Mail; /// /// What a sync pass has to do in one folder, worked out from what the index holds and what the server reports. /// /// /// The server is asked which mails belong into the index -- those of the period, and every flagged /// one -- and the difference to the index says the rest. A mail which was deleted, moved away, fell /// out of the period or lost its flag drops out alike; that it may lie elsewhere still is found out /// by its key, not here. /// /// Whether the server voided every UID of the folder. The stored locations go, and every mail counts as new: the ones the index holds are linked again by their key, without being embedded again. /// The UIDs the index does not hold for this folder, the newest first. /// The UIDs the index holds for this folder which no longer belong into it, in ascending order. /// The UIDs the index holds and keeps, whose flags may have changed. /// Whether the flags of the kept mails have to be fetched at all. /// Asks only about mails changed since this HIGHESTMODSEQ, or null to ask about every kept mail. public sealed record MailFolderSyncPlan(bool UidValidityChanged, IReadOnlyList NewUids, IReadOnlyList GoneUids, IReadOnlyList KeptUids, bool ChecksFlags, long? FlagsChangedSinceModSeq) { /// /// Works out the plan for one folder. /// /// The folder as the index stores it, or null when the index does not know it yet. /// The UIDs the index holds for the folder, with their flags. /// How the folder stands on the server. /// The UIDs which belong into the index, as the server found them. /// The plan. public static MailFolderSyncPlan Create(MailFolderRecord? storedFolder, IReadOnlyDictionary storedLocations, MailFolderState serverState, IReadOnlyCollection indexedUids) { var uidValidityChanged = storedFolder is not null && storedFolder.UidValidity != serverState.UidValidity; HashSet knownUids = uidValidityChanged ? [] : storedLocations.Keys.ToHashSet(); var wantedUids = indexedUids.ToHashSet(); var newUids = wantedUids.Where(uid => !knownUids.Contains(uid)).OrderDescending().ToList(); var goneUids = knownUids.Where(uid => !wantedUids.Contains(uid)).Order().ToList(); var keptUids = knownUids.Where(wantedUids.Contains).Order().ToList(); // // With CONDSTORE, the HIGHESTMODSEQ of the last complete pass says whether any flag changed // since, and if so, the server names the changed mails itself. Without it, or after a pass // which did not complete, only fetching every flag tells. // if (keptUids.Count is 0) return new(uidValidityChanged, newUids, goneUids, keptUids, false, null); if (storedFolder?.HighestModSeq is { } storedModSeq && serverState.HighestModSeq is { } currentModSeq) return new(uidValidityChanged, newUids, goneUids, keptUids, currentModSeq != storedModSeq, storedModSeq); return new(uidValidityChanged, newUids, goneUids, keptUids, true, null); } /// /// Picks the flags which differ from what the index holds. /// /// The UIDs the index holds for the folder, with their flags. /// The flags the server reported. /// The changed flags by UID. A UID the index does not hold is left out. public static IReadOnlyDictionary GetChangedFlags(IReadOnlyDictionary storedLocations, IReadOnlyDictionary fetchedFlags) => fetchedFlags .Where(entry => storedLocations.TryGetValue(entry.Key, out var storedFlags) && storedFlags != entry.Value) .ToDictionary(entry => entry.Key, entry => entry.Value); }