Skip to main content

tuwunel_service/rooms/spaces/
federation.rs

1use 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, trace};
11
12use super::{
13	Accessibility,
14	Accessibility::{Accessible, Inaccessible},
15	Identifier,
16};
17
18/// Gets the summary of a space using solely federation.
19#[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 Some(Ok(Response { room, children, inaccessible_children })) = requests.next().await
57	else {
58		self.cache_put(current_room, None);
59		return Err!(Request(NotFound("Space room not found over federation.")));
60	};
61
62	trace!(
63		?current_room,
64		?sender,
65		?room,
66		?children,
67		?inaccessible_children,
68		"federation response"
69	);
70
71	for room_id in &inaccessible_children {
72		self.cache_put(room_id, None);
73	}
74
75	for summary in children
76		.into_iter()
77		.filter(|child| child.room_type.ne(&Some(RoomType::Space)))
78	{
79		let room_id = summary.room_id.clone();
80		let summary = ParentSummary {
81			summary,
82			children_state: Default::default(),
83		};
84
85		self.cache_put(&room_id, Some(&summary));
86	}
87
88	self.cache_put(current_room, Some(&room));
89
90	self.is_accessible_child(current_room, &room.summary.join_rule.clone(), sender)
91		.await
92		.then(|| Ok(Accessible(room)))
93		.unwrap_or(Ok(Inaccessible))
94}