Skip to main content

tuwunel_api_client/sync/v5/rooms/
collected.rs

1//! What one delivery read of a room's required state.
2//!
3//! The result decides the snapshot the delivery records and the keys it leaves
4//! for the next delivery to send.
5
6use tuwunel_core::{implement, utils::BoolExt};
7use tuwunel_service::{rooms::short::ShortStateHash, sync::UnreadState};
8
9use super::{CollectedState, StateEvent};
10
11/// One selected key's outcome: its event, if sent, or why it was left out.
12///
13/// A sent event carries its key's fingerprint when the last delivery had left
14/// that key unread.
15pub(super) type Outcome = Result<Option<(StateEvent, Option<u64>)>, Unread>;
16
17/// Why a selected key's event was left out of a delivery, when not by choice.
18///
19/// A due event that could not be read leaves its key for the next delivery to
20/// send; a key that could not be resolved makes the delivery record zero.
21pub(super) enum Unread {
22	/// The key could not be resolved, so whether its event was due is unknown.
23	Unresolved,
24
25	/// The event was due but could not be read; carries its key's fingerprint.
26	Unreadable(u64),
27}
28
29/// The required state a delivery read, folded from its candidates.
30///
31/// A selected key adds its event when sent, its fingerprint when its due event
32/// could not be read, or the mark of an unresolved key. A sent event whose key
33/// the last delivery left unread also adds that key's fingerprint.
34#[derive(Default)]
35pub(super) struct Collected {
36	/// The events read.
37	pub(super) events: Vec<StateEvent>,
38
39	/// Fingerprints of the keys whose due events could not be read.
40	pub(super) unread: UnreadState,
41
42	/// Fingerprints of keys the last delivery left unread whose events were sent.
43	pub(super) settled: UnreadState,
44
45	/// Whether a key could not be resolved.
46	pub(super) unresolved: bool,
47}
48
49#[implement(Collected)]
50pub(super) fn push(mut self, outcome: Outcome) -> Self {
51	match outcome {
52		| Ok(None) => {},
53		| Err(Unread::Unreadable(fingerprint)) => self.unread.push(fingerprint),
54		| Err(Unread::Unresolved) => self.unresolved = true,
55		| Ok(Some((event, settled))) => {
56			self.events.push(event);
57			self.settled.extend(settled);
58		},
59	}
60
61	self
62}
63
64/// The events read, the snapshot the delivery records, and the keys it leaves
65/// unread.
66///
67/// `previous` holds the keys the last delivery left unread. A complete delivery,
68/// one that compared every selected key against `snapshot` and resolved them
69/// all, keeps none of them; any other keeps those whose events it did not send.
70#[implement(Collected)]
71pub(super) fn finish(self, snapshot: Option<ShortStateHash>, previous: &[u64]) -> CollectedState {
72	let resolved = self.unresolved.is_false();
73	let complete = snapshot.is_some() && resolved;
74	let snapshot = recorded_snapshot(snapshot, resolved);
75	let previous = complete
76		.is_false()
77		.then_some(previous)
78		.unwrap_or_default();
79
80	let kept = previous
81		.iter()
82		.copied()
83		.filter(|fingerprint| !self.settled.contains(fingerprint));
84
85	let unread = unread_state(kept, self.unread);
86
87	(self.events, (snapshot, unread))
88}
89
90/// The snapshot a delivery records, given whether it resolved every selected key.
91///
92/// A key whose listing or current event could not be read may hide an event that
93/// was due, so no snapshot vouches for what the client holds: the delivery
94/// records zero, and the next delta compares against the cursor and resends
95/// outliers.
96pub(super) fn recorded_snapshot(
97	snapshot: Option<ShortStateHash>,
98	resolved: bool,
99) -> Option<ShortStateHash> {
100	resolved.copy_or(Some(0), snapshot)
101}
102
103/// The keys the next delivery sends whatever snapshot it compares against.
104///
105/// They are the keys whose due events this delivery could not read, and the
106/// `kept` keys an earlier delivery left unread, each once and in order.
107pub(super) fn unread_state(
108	kept: impl Iterator<Item = u64>,
109	mut unread: UnreadState,
110) -> UnreadState {
111	unread.extend(kept);
112	unread.sort_unstable();
113	unread.dedup();
114	unread.shrink_to_fit();
115	unread
116}