tuwunel_service/federation/feds/
fold.rs1use std::collections::{BTreeMap, BTreeSet};
8
9use futures::{Stream, pin_mut};
10use ruma::OwnedServerName;
11use tuwunel_core::utils::stream::ReadyExt;
12
13use super::{Fault, Outcome};
14
15pub type Origins = BTreeSet<OwnedServerName>;
19
20pub type Faults = BTreeMap<OwnedServerName, Fault>;
24
25pub struct Grid<K> {
30 pub data: BTreeMap<K, Origins>,
34
35 pub empty: Origins,
39
40 pub faults: Faults,
44}
45
46pub struct Tally {
51 pub ok: Origins,
55
56 pub faults: Faults,
60}
61
62pub trait OutcomeExt<R>
67where
68 Self: Stream<Item = Outcome<R>> + Send + Sized,
69 R: Send,
70{
71 fn merge<T, F>(self, init: T, merge: F) -> impl Future<Output = (T, Faults)> + Send
75 where
76 T: Send,
77 F: Fn(T, R) -> T + Send;
78
79 fn grid<K, I, F>(self, extract: F) -> impl Future<Output = Grid<K>> + Send
83 where
84 K: Ord + Send,
85 I: IntoIterator<Item = K>,
86 F: Fn(R) -> I + Send;
87
88 fn tally(self) -> impl Future<Output = Tally> + Send;
92
93 fn first_acceptable<F>(
97 self,
98 accept: F,
99 ) -> impl Future<Output = Option<(OwnedServerName, R)>> + Send
100 where
101 F: Fn(&R) -> bool + Send;
102}
103
104impl<S, R> OutcomeExt<R> for S
105where
106 S: Stream<Item = Outcome<R>> + Send + Sized,
107 R: Send,
108{
109 fn merge<T, F>(self, init: T, merge: F) -> impl Future<Output = (T, Faults)> + Send
110 where
111 T: Send,
112 F: Fn(T, R) -> T + Send,
113 {
114 self.ready_fold((init, Faults::new()), move |(merged, mut faults), outcome| match outcome
115 .result
116 {
117 | Ok(response) => (merge(merged, response), faults),
118 | Err(fault) => {
119 faults.insert(outcome.origin, fault);
120 (merged, faults)
121 },
122 })
123 }
124
125 fn grid<K, I, F>(self, extract: F) -> impl Future<Output = Grid<K>> + Send
126 where
127 K: Ord + Send,
128 I: IntoIterator<Item = K>,
129 F: Fn(R) -> I + Send,
130 {
131 let grid = Grid {
132 data: BTreeMap::new(),
133 empty: Origins::new(),
134 faults: Faults::new(),
135 };
136
137 self.ready_fold(grid, move |mut grid, outcome| {
138 match outcome.result {
139 | Ok(response) => {
140 let mut data = extract(response).into_iter();
141
142 if let Some(mut datum) = data.next() {
143 for next in data {
144 grid.data
145 .entry(datum)
146 .or_default()
147 .insert(outcome.origin.clone());
148
149 datum = next;
150 }
151
152 grid.data
153 .entry(datum)
154 .or_default()
155 .insert(outcome.origin);
156 } else {
157 grid.empty.insert(outcome.origin);
158 }
159 },
160 | Err(fault) => {
161 grid.faults.insert(outcome.origin, fault);
162 },
163 }
164
165 grid
166 })
167 }
168
169 fn tally(self) -> impl Future<Output = Tally> + Send {
170 let tally = Tally {
171 ok: Origins::new(),
172 faults: Faults::new(),
173 };
174
175 self.ready_fold(tally, |mut tally, outcome| {
176 match outcome.result {
177 | Ok(_response) => {
178 tally.ok.insert(outcome.origin);
179 },
180 | Err(fault) => {
181 tally.faults.insert(outcome.origin, fault);
182 },
183 }
184
185 tally
186 })
187 }
188
189 async fn first_acceptable<F>(self, accept: F) -> Option<(OwnedServerName, R)>
190 where
191 F: Fn(&R) -> bool + Send,
192 {
193 let outcomes = self;
194
195 pin_mut!(outcomes);
196 outcomes
197 .ready_find_map(move |outcome| match outcome.result {
198 | Ok(response) if accept(&response) => Some((outcome.origin, response)),
199 | _ => None,
200 })
201 .await
202 }
203}