tuwunel_service/rooms/spaces/
federation.rs1use futures::{StreamExt, pin_mut, stream::FuturesUnordered};
2use ruma::{
3 OwnedServerName, RoomId,
4 api::federation::space::{
5 SpaceHierarchyParentSummary as ParentSummary,
6 get_hierarchy::v1::{Request, Response},
7 },
8 room::RoomType,
9};
10use tuwunel_core::{Err, Result, debug, implement};
11
12use super::{
13 Accessibility,
14 Accessibility::{Accessible, Inaccessible},
15 Identifier,
16};
17
18#[implement(super::Service)]
20#[tracing::instrument(
21 name = "federation",
22 level = "debug",
23 err(level = "debug"),
24 ret(level = "trace"),
25 skip(self)
26)]
27pub(super) async fn get_summary_and_children_federation(
28 &self,
29 current_room: &RoomId,
30 sender: &Identifier<'_>,
31 via: &[OwnedServerName],
32) -> Result<Accessibility> {
33 let request = Request {
34 room_id: current_room.to_owned(),
35 suggested_only: false,
36 };
37
38 let requests: FuturesUnordered<_> = via
39 .iter()
40 .map(|server| {
41 self.services
42 .federation
43 .execute(server, request.clone())
44 })
45 .collect();
46
47 pin_mut!(requests);
48 debug!(
49 ?current_room,
50 ?sender,
51 ?via,
52 requests = requests.len(),
53 "waiting for federation response"
54 );
55
56 let mut response = None;
57 while let Some(result) = requests.next().await {
58 match result {
59 | Ok(ok_response) => {
60 debug!(?ok_response, "federation response");
61
62 response = Some(ok_response);
63 break;
64 },
65 | Err(error) => {
66 debug!(?error, "federation error");
67 },
68 }
69 }
70
71 let Some(Response { room, children, inaccessible_children }) = response else {
72 self.cache_put(current_room, None);
73 return Err!(Request(NotFound("Space room not found over federation.")));
74 };
75
76 for room_id in &inaccessible_children {
77 self.cache_put(room_id, None);
78 }
79
80 for summary in children
81 .into_iter()
82 .filter(|child| child.room_type.ne(&Some(RoomType::Space)))
83 {
84 let room_id = summary.room_id.clone();
85 let summary = ParentSummary {
86 summary,
87 children_state: Default::default(),
88 };
89
90 self.cache_put(&room_id, Some(&summary));
91 }
92
93 self.cache_put(current_room, Some(&room));
94
95 self.is_accessible_child(current_room, &room.summary.join_rule.clone(), sender)
96 .await
97 .then(|| Ok(Accessible(room)))
98 .unwrap_or(Ok(Inaccessible))
99}