1use std::num::NonZeroUsize;
7
8use bytes::Bytes;
9use ruma::{
10 MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedRoomId, OwnedServerName, RoomVersionId,
11 api::Direction,
12};
13use tuwunel_core::smallvec::SmallVec;
14
15use crate::federation::Candidates;
16
17pub type EventWindow = SmallVec<[OwnedEventId; 1]>;
20
21#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
24pub enum Op {
25 Event,
27
28 AuthEvent,
32
33 AuthChain,
35
36 Backfill,
38
39 StateIds,
41
42 MissingEvents,
44
45 TimestampToEvent,
47}
48
49#[derive(Clone, Copy, Debug, Eq, PartialEq)]
55pub enum FanoutGrowth {
56 Fixed(NonZeroUsize),
58
59 Linear {
61 base: NonZeroUsize,
62 step: NonZeroUsize,
63 },
64
65 Geometric {
68 base: NonZeroUsize,
69 factor: NonZeroUsize,
70 },
71}
72
73impl FanoutGrowth {
74 #[must_use]
78 pub fn round_width(self, round: usize) -> usize {
79 match self {
80 | Self::Fixed(width) => width.get(),
81 | Self::Linear { base, step } => base
82 .get()
83 .saturating_add(step.get().saturating_mul(round)),
84 | Self::Geometric { base, factor } => {
85 let exp = u32::try_from(round).unwrap_or(u32::MAX);
86
87 base.get()
88 .saturating_mul(factor.get().saturating_pow(exp))
89 },
90 }
91 }
92}
93
94#[derive(Clone, Debug)]
98pub struct Opts {
99 pub op: Op,
101
102 pub room_id: Option<OwnedRoomId>,
105
106 pub event_id: Option<OwnedEventId>,
108
109 pub ts: Option<MilliSecondsSinceUnixEpoch>,
112
113 pub dir: Option<Direction>,
116
117 pub earliest_events: EventWindow,
120
121 pub latest_events: EventWindow,
124
125 pub hint: Option<OwnedServerName>,
127
128 pub candidates: Candidates,
131
132 pub room_version: Option<RoomVersionId>,
134
135 pub attempt_limit: Option<NonZeroUsize>,
137
138 pub backfill_limit: Option<NonZeroUsize>,
141
142 pub fanout_growth: FanoutGrowth,
144
145 pub fanout_max_width: Option<NonZeroUsize>,
149
150 pub fanout_rounds: Option<NonZeroUsize>,
152
153 pub check_event_id: bool,
155
156 pub check_conforms: bool,
158
159 pub check_hashes: bool,
161
162 pub authoritative_redaction: bool,
165
166 pub check_signature: bool,
168}
169
170impl Opts {
171 #[must_use]
173 pub fn new(op: Op, room_id: OwnedRoomId) -> Self { Self::with_room_id(op, Some(room_id)) }
174
175 #[must_use]
179 pub fn unscoped(op: Op) -> Self { Self::with_room_id(op, None) }
180
181 fn with_room_id(op: Op, room_id: Option<OwnedRoomId>) -> Self {
183 Self {
184 op,
185 room_id,
186 event_id: None,
187 ts: None,
188 dir: None,
189 earliest_events: EventWindow::new(),
190 latest_events: EventWindow::new(),
191 hint: None,
192 candidates: Candidates::new(),
193 room_version: None,
194 attempt_limit: None,
195 backfill_limit: None,
196 fanout_growth: FanoutGrowth::Fixed(NonZeroUsize::MIN),
197 fanout_max_width: None,
198 fanout_rounds: None,
199 check_event_id: true,
200 check_conforms: true,
201 check_hashes: true,
202 authoritative_redaction: true,
203 check_signature: true,
204 }
205 }
206
207 #[must_use]
209 pub fn event_id(self, event_id: OwnedEventId) -> Self {
210 Self { event_id: Some(event_id), ..self }
211 }
212
213 #[must_use]
215 pub fn ts(self, ts: MilliSecondsSinceUnixEpoch) -> Self { Self { ts: Some(ts), ..self } }
216
217 #[must_use]
219 pub fn dir(self, dir: Direction) -> Self { Self { dir: Some(dir), ..self } }
220
221 #[must_use]
223 pub fn earliest_events<I>(self, earliest_events: I) -> Self
224 where
225 I: IntoIterator<Item = OwnedEventId>,
226 {
227 Self {
228 earliest_events: earliest_events.into_iter().collect(),
229 ..self
230 }
231 }
232
233 #[must_use]
235 pub fn latest_events<I>(self, latest_events: I) -> Self
236 where
237 I: IntoIterator<Item = OwnedEventId>,
238 {
239 Self {
240 latest_events: latest_events.into_iter().collect(),
241 ..self
242 }
243 }
244
245 #[must_use]
247 pub fn hint(self, hint: OwnedServerName) -> Self { Self { hint: Some(hint), ..self } }
248
249 #[must_use]
251 pub fn candidates<I>(self, candidates: I) -> Self
252 where
253 I: IntoIterator<Item = OwnedServerName>,
254 {
255 Self {
256 candidates: candidates.into_iter().collect(),
257 ..self
258 }
259 }
260
261 #[must_use]
265 pub fn room_version(self, room_version: RoomVersionId) -> Self {
266 Self { room_version: Some(room_version), ..self }
267 }
268
269 #[must_use]
271 pub fn attempt_limit(self, attempt_limit: NonZeroUsize) -> Self {
272 Self {
273 attempt_limit: Some(attempt_limit),
274 ..self
275 }
276 }
277
278 #[must_use]
281 pub fn backfill_limit(self, backfill_limit: NonZeroUsize) -> Self {
282 Self {
283 backfill_limit: Some(backfill_limit),
284 ..self
285 }
286 }
287
288 #[must_use]
290 pub fn fanout(self, growth: FanoutGrowth) -> Self { Self { fanout_growth: growth, ..self } }
291
292 #[must_use]
294 pub fn fanout_max_width(self, max_width: NonZeroUsize) -> Self {
295 Self {
296 fanout_max_width: Some(max_width),
297 ..self
298 }
299 }
300
301 #[must_use]
303 pub fn fanout_rounds(self, rounds: NonZeroUsize) -> Self {
304 Self { fanout_rounds: Some(rounds), ..self }
305 }
306
307 #[must_use]
311 pub fn fanout_for_op(self) -> Self {
312 use FanoutGrowth::{Geometric, Linear};
313
314 const ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
315 const TWO: NonZeroUsize = NonZeroUsize::new(2).unwrap();
316 const THREE: NonZeroUsize = NonZeroUsize::new(3).unwrap();
317 const FOUR: NonZeroUsize = NonZeroUsize::new(4).unwrap();
318 const FIVE: NonZeroUsize = NonZeroUsize::new(5).unwrap();
319
320 match self.op {
321 | Op::AuthEvent => self
322 .fanout(Geometric { base: ONE, factor: TWO })
323 .fanout_max_width(FOUR)
324 .fanout_rounds(FIVE),
325 | Op::AuthChain => self
326 .fanout(Linear { base: ONE, step: ONE })
327 .fanout_max_width(TWO)
328 .fanout_rounds(TWO),
329 | Op::StateIds => self
330 .fanout(Linear { base: ONE, step: ONE })
331 .fanout_max_width(THREE)
332 .fanout_rounds(THREE),
333 | Op::MissingEvents => self
334 .fanout(Geometric { base: ONE, factor: TWO })
335 .fanout_rounds(THREE),
336 | Op::Event | Op::Backfill | Op::TimestampToEvent => self,
337 }
338 }
339
340 #[must_use]
344 pub fn checks(self, enabled: bool) -> Self {
345 Self {
346 check_event_id: enabled,
347 check_conforms: enabled,
348 check_hashes: enabled,
349 check_signature: enabled,
350 ..self
351 }
352 }
353}
354
355#[derive(Debug)]
358pub struct Outcome {
359 pub bytes: Bytes,
360 pub origin: OwnedServerName,
361}