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)]
54pub enum FanoutGrowth {
55 Fixed(NonZeroUsize),
57
58 Linear {
60 base: NonZeroUsize,
61 step: NonZeroUsize,
62 },
63
64 Geometric {
67 base: NonZeroUsize,
68 factor: NonZeroUsize,
69 },
70}
71
72impl FanoutGrowth {
73 #[must_use]
77 pub fn round_width(self, round: usize) -> usize {
78 match self {
79 | Self::Fixed(width) => width.get(),
80 | Self::Linear { base, step } => base
81 .get()
82 .saturating_add(step.get().saturating_mul(round)),
83 | Self::Geometric { base, factor } => {
84 let exp = u32::try_from(round).unwrap_or(u32::MAX);
85
86 base.get()
87 .saturating_mul(factor.get().saturating_pow(exp))
88 },
89 }
90 }
91}
92
93#[derive(Clone, Debug)]
97pub struct Opts {
98 pub op: Op,
100
101 pub room_id: Option<OwnedRoomId>,
104
105 pub event_id: Option<OwnedEventId>,
107
108 pub ts: Option<MilliSecondsSinceUnixEpoch>,
111
112 pub dir: Option<Direction>,
115
116 pub earliest_events: EventWindow,
119
120 pub latest_events: EventWindow,
123
124 pub hint: Option<OwnedServerName>,
126
127 pub candidates: Candidates,
130
131 pub room_version: Option<RoomVersionId>,
133
134 pub attempt_limit: Option<NonZeroUsize>,
136
137 pub backfill_limit: Option<NonZeroUsize>,
140
141 pub fanout_growth: FanoutGrowth,
143
144 pub fanout_max_width: Option<NonZeroUsize>,
148
149 pub fanout_rounds: Option<NonZeroUsize>,
151
152 pub check_event_id: bool,
154
155 pub check_conforms: bool,
157
158 pub check_hashes: bool,
160
161 pub authoritative_redaction: bool,
164
165 pub check_signature: bool,
167}
168
169impl Opts {
170 #[must_use]
172 pub fn new(op: Op, room_id: OwnedRoomId) -> Self { Self::with_room_id(op, Some(room_id)) }
173
174 #[must_use]
178 pub fn unscoped(op: Op) -> Self { Self::with_room_id(op, None) }
179
180 fn with_room_id(op: Op, room_id: Option<OwnedRoomId>) -> Self {
182 Self {
183 op,
184 room_id,
185 event_id: None,
186 ts: None,
187 dir: None,
188 earliest_events: EventWindow::new(),
189 latest_events: EventWindow::new(),
190 hint: None,
191 candidates: Candidates::new(),
192 room_version: None,
193 attempt_limit: None,
194 backfill_limit: None,
195 fanout_growth: FanoutGrowth::Fixed(NonZeroUsize::MIN),
196 fanout_max_width: None,
197 fanout_rounds: None,
198 check_event_id: true,
199 check_conforms: true,
200 check_hashes: true,
201 authoritative_redaction: true,
202 check_signature: true,
203 }
204 }
205
206 #[must_use]
208 pub fn event_id(self, event_id: OwnedEventId) -> Self {
209 Self { event_id: Some(event_id), ..self }
210 }
211
212 #[must_use]
214 pub fn ts(self, ts: MilliSecondsSinceUnixEpoch) -> Self { Self { ts: Some(ts), ..self } }
215
216 #[must_use]
218 pub fn dir(self, dir: Direction) -> Self { Self { dir: Some(dir), ..self } }
219
220 #[must_use]
222 pub fn earliest_events<I>(self, earliest_events: I) -> Self
223 where
224 I: IntoIterator<Item = OwnedEventId>,
225 {
226 Self {
227 earliest_events: earliest_events.into_iter().collect(),
228 ..self
229 }
230 }
231
232 #[must_use]
234 pub fn latest_events<I>(self, latest_events: I) -> Self
235 where
236 I: IntoIterator<Item = OwnedEventId>,
237 {
238 Self {
239 latest_events: latest_events.into_iter().collect(),
240 ..self
241 }
242 }
243
244 #[must_use]
246 pub fn hint(self, hint: OwnedServerName) -> Self { Self { hint: Some(hint), ..self } }
247
248 #[must_use]
250 pub fn candidates<I>(self, candidates: I) -> Self
251 where
252 I: IntoIterator<Item = OwnedServerName>,
253 {
254 Self {
255 candidates: candidates.into_iter().collect(),
256 ..self
257 }
258 }
259
260 #[must_use]
264 pub fn room_version(self, room_version: RoomVersionId) -> Self {
265 Self { room_version: Some(room_version), ..self }
266 }
267
268 #[must_use]
270 pub fn attempt_limit(self, attempt_limit: NonZeroUsize) -> Self {
271 Self {
272 attempt_limit: Some(attempt_limit),
273 ..self
274 }
275 }
276
277 #[must_use]
280 pub fn backfill_limit(self, backfill_limit: NonZeroUsize) -> Self {
281 Self {
282 backfill_limit: Some(backfill_limit),
283 ..self
284 }
285 }
286
287 #[must_use]
289 pub fn fanout(self, growth: FanoutGrowth) -> Self { Self { fanout_growth: growth, ..self } }
290
291 #[must_use]
293 pub fn fanout_max_width(self, max_width: NonZeroUsize) -> Self {
294 Self {
295 fanout_max_width: Some(max_width),
296 ..self
297 }
298 }
299
300 #[must_use]
302 pub fn fanout_rounds(self, rounds: NonZeroUsize) -> Self {
303 Self { fanout_rounds: Some(rounds), ..self }
304 }
305
306 #[must_use]
310 pub fn fanout_for_op(self) -> Self {
311 use FanoutGrowth::{Geometric, Linear};
312
313 const ONE: NonZeroUsize = NonZeroUsize::new(1).unwrap();
314 const TWO: NonZeroUsize = NonZeroUsize::new(2).unwrap();
315 const THREE: NonZeroUsize = NonZeroUsize::new(3).unwrap();
316 const FOUR: NonZeroUsize = NonZeroUsize::new(4).unwrap();
317 const FIVE: NonZeroUsize = NonZeroUsize::new(5).unwrap();
318
319 match self.op {
320 | Op::AuthEvent => self
321 .fanout(Geometric { base: ONE, factor: TWO })
322 .fanout_max_width(FOUR)
323 .fanout_rounds(FIVE),
324 | Op::AuthChain => self
325 .fanout(Linear { base: ONE, step: ONE })
326 .fanout_max_width(TWO)
327 .fanout_rounds(TWO),
328 | Op::StateIds => self
329 .fanout(Linear { base: ONE, step: ONE })
330 .fanout_max_width(THREE)
331 .fanout_rounds(THREE),
332 | Op::MissingEvents => self
333 .fanout(Geometric { base: ONE, factor: TWO })
334 .fanout_rounds(THREE),
335 | Op::Event | Op::Backfill | Op::TimestampToEvent => self,
336 }
337 }
338
339 #[must_use]
343 pub fn checks(self, enabled: bool) -> Self {
344 Self {
345 check_event_id: enabled,
346 check_conforms: enabled,
347 check_hashes: enabled,
348 check_signature: enabled,
349 ..self
350 }
351 }
352}
353
354#[derive(Debug)]
357pub struct Outcome {
358 pub bytes: Bytes,
359 pub origin: OwnedServerName,
360}