Skip to main content

arminius_lib_sync/syncv4/plan/
mod.rs

1// SPDX-FileCopyrightText: 2026 BWI GmbH
2//
3// SPDX-License-Identifier: Apache-2.0
4
5//! Contains the logic to **plan** a sync v4 request.
6
7mod apply_request;
8mod apply_state;
9
10use std::collections::HashMap;
11
12pub use apply_request::{FilteredRooms, MatchedRoom};
13pub use apply_state::RoomToSync;
14use arminius_lib_db_sync::syncv4::RoomState;
15use itertools::Itertools as _;
16use ruma::{OwnedRoomId, api::client::sync::sync_events};
17
18use crate::syncv4::{
19	connect::ResolvedConnection, error::SyncV4Error, gather, plan::apply_state::AppliedState,
20};
21
22/// Contains a sync v4 plan, i.e. the result of [`plan`] over a [gather::SyncV4Inputs].
23#[derive(Debug)]
24pub struct SyncV4Plan {
25	/// The connection of the current request.
26	pub(crate) connection: ResolvedConnection,
27	/// Bump stamp of each of the user's room.
28	pub(crate) rooms_bump_stamps: HashMap<OwnedRoomId, usize>,
29
30	/// The updated state to store for the current response.
31	pub(crate) updated_state: HashMap<OwnedRoomId, RoomState>,
32
33	/// The counts of rooms matched by each list.
34	pub(crate) lists_counts: HashMap<String, usize>,
35
36	/// List of rooms that are now stale (see [`apply_state::apply_state`]).
37	pub(crate) stale_rooms: Vec<OwnedRoomId>,
38
39	/// The map of rooms that will be part of the current sync.
40	pub(crate) rooms_to_sync: HashMap<OwnedRoomId, RoomToSync>,
41}
42
43impl SyncV4Plan {
44	/// Tells whether the computed plan actually contains data to sync or not.
45	pub fn has_data_to_sync(&self) -> bool {
46		!self.rooms_to_sync.is_empty()
47		// TODO: Add checks for whether the extensions data are empty or not
48	}
49}
50
51/// Plan a sync v4 request by applying the request's as well as the per-connection state to the
52/// user's rooms and produce a plan out of it (see [SyncV4Plan]).
53pub(super) fn plan(
54	inputs: gather::SyncV4Inputs,
55	request: &sync_events::v5::Request,
56) -> Result<SyncV4Plan, SyncV4Error> {
57	let gather::SyncV4Inputs { connection, user_rooms, mut rooms_states } = inputs;
58	let prev_events_stream_id = connection.prev_events_stream_id().unwrap_or(0);
59
60	let FilteredRooms { lists_counts, matched_rooms } =
61		apply_request::apply_request(&user_rooms, &request.lists, &request.room_subscriptions)?;
62
63	let AppliedState { rooms_to_sync, stale_rooms } = apply_state::apply_state(
64		prev_events_stream_id,
65		&matched_rooms,
66		&user_rooms,
67		&mut rooms_states,
68	);
69
70	// Compute the bump stamp of each of the user's room
71	let rooms_bump_stamps: HashMap<OwnedRoomId, usize> = user_rooms
72		.into_iter()
73		.sorted_by_key(|(_room_id, room_info)| std::cmp::Reverse(room_info.bump_event_stream_id))
74		.enumerate()
75		.map(|(bump_stamp, (room_id, _room_info))| (room_id, bump_stamp))
76		// Might as well retain only those that we'll need
77		.filter(|(room_id, _bump_stamp)| rooms_to_sync.contains_key(room_id))
78		.collect();
79
80	Ok(SyncV4Plan {
81		connection,
82		rooms_bump_stamps,
83		updated_state: rooms_states,
84		lists_counts,
85		stale_rooms,
86		rooms_to_sync,
87	})
88}