tuwunel_service/rooms/state_res/resolve/
auth_difference.rs1use std::{borrow::Borrow, collections::HashMap, hash::Hash};
2
3use futures::{FutureExt, Stream};
4use ruma::EventId;
5use tuwunel_core::{
6 matrix::event_id::RandomState,
7 utils::stream::{IterStream, ReadyExt},
8};
9
10use super::AuthSet;
11
12struct Counts<Id> {
13 by_id: HashMap<Id, usize, RandomState>,
14 total: usize,
15}
16
17impl<Id> Default for Counts<Id> {
18 fn default() -> Self { Self { by_id: HashMap::default(), total: 0 } }
19}
20
21impl<Id: Eq + Hash> Counts<Id> {
22 fn merge(mut self, set: AuthSet<Id>) -> Self {
23 self.total = self.total.saturating_add(1);
24 for id in set {
25 let count = self.by_id.entry(id).or_default();
26
27 *count = count.saturating_add(1);
28 }
29
30 self
31 }
32}
33
34#[tracing::instrument(level = "debug", skip_all)]
54pub(super) fn auth_difference<'a, AuthSets, Id>(auth_sets: AuthSets) -> impl Stream<Item = Id>
55where
56 AuthSets: Stream<Item = AuthSet<Id>>,
57 Id: Borrow<EventId> + Clone + Eq + Hash + Send + 'a,
58{
59 auth_sets
60 .ready_fold_default(Counts::<Id>::merge)
61 .map(|Counts { by_id, total }: Counts<Id>| {
62 by_id
63 .into_iter()
64 .filter_map(move |(id, count)| (count < total).then_some(id))
65 .stream()
66 })
67 .flatten_stream()
68}