tuwunel_service/rooms/state_res/resolve/
mainline_sort.rs1use std::collections::HashMap;
2
3use futures::{
4 FutureExt, Stream, StreamExt, TryFutureExt, TryStreamExt, pin_mut, stream::try_unfold,
5};
6use ruma::{EventId, OwnedEventId, events::TimelineEventType};
7use tuwunel_core::{
8 Error, Result, at,
9 matrix::{Event, event_id::RandomState},
10 trace,
11 utils::stream::{BroadbandExt, IterStream, TryReadyExt},
12};
13
14type Positions<'a> = HashMap<&'a EventId, usize, RandomState>;
16
17#[tracing::instrument(
43 level = "debug",
44 skip_all,
45 fields(
46 power_levels = power_level_event_id
47 .as_deref()
48 .map(EventId::as_str)
49 .unwrap_or_default(),
50 )
51)]
52pub(super) async fn mainline_sort<'a, RemainingEvents, Fetch, Fut, Pdu>(
53 power_level_event_id: Option<OwnedEventId>,
54 events: RemainingEvents,
55 fetch: &Fetch,
56) -> Result<Vec<OwnedEventId>>
57where
58 RemainingEvents: Stream<Item = &'a EventId> + Send,
59 Fetch: Fn(OwnedEventId) -> Fut + Sync,
60 Fut: Future<Output = Result<Pdu>> + Send,
61 Pdu: Event,
62{
63 let mainline: Vec<_> = try_unfold(power_level_event_id, async |power_level_event_id| {
65 let Some(power_level_event_id) = power_level_event_id else {
66 return Ok::<_, Error>(None);
67 };
68
69 let power_level_event = fetch(power_level_event_id).await?;
70 let this_event_id = power_level_event.event_id().to_owned();
71 let next_event_id = get_power_levels_auth_event(&power_level_event, fetch)
72 .map_ok(|event| {
73 event
74 .as_ref()
75 .map(Event::event_id)
76 .map(ToOwned::to_owned)
77 })
78 .await?;
79
80 trace!(?this_event_id, ?next_event_id, "mainline descent",);
81
82 Ok(Some((this_event_id, next_event_id)))
83 })
84 .try_collect()
85 .await?;
86
87 let positions: Positions<'_> = mainline
88 .iter()
89 .rev()
90 .map(AsRef::as_ref)
91 .enumerate()
92 .map(|(position, event_id)| (event_id, position))
93 .collect();
94
95 events
96 .map(ToOwned::to_owned)
97 .broad_filter_map(async |event_id| {
98 let event = fetch(event_id.clone()).await.ok()?;
99 let origin_server_ts = event.origin_server_ts();
100 let position = mainline_position(Some(event), &positions, fetch)
101 .await
102 .ok()?;
103
104 Some((event_id, (position, origin_server_ts)))
105 })
106 .inspect(|(event_id, (position, origin_server_ts))| {
107 trace!(position, ?origin_server_ts, ?event_id, "mainline position");
108 })
109 .collect()
110 .map(|mut vec: Vec<_>| {
111 vec.sort_by(|a, b| {
112 let (a_pos, a_ots) = &a.1;
113 let (b_pos, b_ots) = &b.1;
114 a_pos
115 .cmp(b_pos)
116 .then(a_ots.cmp(b_ots))
117 .then(a.cmp(b))
118 });
119
120 vec.into_iter().map(at!(0)).collect()
121 })
122 .map(Ok)
123 .await
124}
125
126#[tracing::instrument(
139 name = "position",
140 level = "trace",
141 ret(level = "trace"),
142 skip_all,
143 fields(
144 mainline = positions.len(),
145 event = ?current_event.as_ref().map(Event::event_id).map(ToOwned::to_owned),
146 )
147)]
148async fn mainline_position<Fetch, Fut, Pdu>(
149 mut current_event: Option<Pdu>,
150 positions: &Positions<'_>,
151 fetch: &Fetch,
152) -> Result<usize>
153where
154 Fetch: Fn(OwnedEventId) -> Fut + Sync,
155 Fut: Future<Output = Result<Pdu>> + Send,
156 Pdu: Event,
157{
158 while let Some(event) = current_event {
159 trace!(
160 event_id = ?event.event_id(),
161 "mainline position search",
162 );
163
164 if let Some(position) = positions.get(event.event_id()) {
168 return Ok(position.saturating_add(1));
169 }
170
171 current_event = get_power_levels_auth_event(&event, fetch).await?;
173 }
174
175 Ok(0)
178}
179
180#[expect(clippy::redundant_closure)]
181#[tracing::instrument(level = "trace", skip_all)]
182async fn get_power_levels_auth_event<Fetch, Fut, Pdu>(
183 event: &Pdu,
184 fetch: &Fetch,
185) -> Result<Option<Pdu>>
186where
187 Fetch: Fn(OwnedEventId) -> Fut + Sync,
188 Fut: Future<Output = Result<Pdu>> + Send,
189 Pdu: Event,
190{
191 let power_level_event = event
192 .auth_events()
193 .try_stream()
194 .map_ok(ToOwned::to_owned)
195 .and_then(|auth_event_id| fetch(auth_event_id))
196 .ready_try_skip_while(|auth_event| {
197 Ok(!auth_event.is_type_and_state_key(&TimelineEventType::RoomPowerLevels, ""))
198 });
199
200 pin_mut!(power_level_event);
201 power_level_event.try_next().await
202}