Skip to main content

tuwunel_matrix/room/state/resolution/
fetch_state.rs

1use std::{collections::BTreeMap, future::ready};
2
3use futures::{FutureExt, TryFutureExt};
4use ruma::{
5	UserId,
6	events::{StateEventType, room::member::MembershipState},
7};
8use tuwunel_core::{Error, Result, err, result::NotFound};
9
10use super::{
11	StateMap,
12	event_auth::auth_input_error,
13	events::{
14		JoinRule, RoomCreateEvent, RoomJoinRulesEvent, RoomMemberEvent, RoomPowerLevelsEvent,
15		RoomThirdPartyInviteEvent, member::RoomMemberEventResultExt,
16	},
17};
18use crate::{Event, PduEvent, StateKey, TypeStateKey};
19
20/// Reads authorization state through a copyable handle.
21///
22/// Implementations may return owned events or borrow events held by the state snapshot.
23pub trait FetchState: Copy + Send + Sync {
24	/// Event representation returned by this state snapshot.
25	///
26	/// Borrowed representations avoid cloning events during authorization.
27	type Pdu: Event;
28
29	/// Reads the event for an exact state tuple.
30	///
31	/// Missing tuples return a not-found error under the snapshot's completeness policy.
32	fn get(
33		self,
34		ty: StateEventType,
35		key: StateKey,
36	) -> impl Future<Output = Result<Self::Pdu>> + Send;
37
38	/// Reads the room creation event.
39	///
40	/// Missing creation state is an authorization input error.
41	fn room_create_event(
42		self,
43	) -> impl Future<Output = Result<RoomCreateEvent<Self::Pdu>>> + Send {
44		self.get(StateEventType::RoomCreate, "".into())
45			.map_err(auth_input_error)
46			.map_ok(RoomCreateEvent::new)
47			.map_err(required("m.room.create"))
48	}
49
50	/// Reads a user membership from the state snapshot.
51	///
52	/// Missing membership follows the member-event defaulting rules.
53	fn user_membership(
54		self,
55		user_id: &UserId,
56	) -> impl Future<Output = Result<MembershipState>> + Send {
57		self.get(StateEventType::RoomMember, user_id.as_str().into())
58			.map_err(auth_input_error)
59			.map_ok(RoomMemberEvent::new)
60			.map(RoomMemberEventResultExt::membership)
61			.map_err(auth_input_error)
62	}
63
64	/// Reads the optional room power levels event.
65	///
66	/// Missing power levels return no event.
67	fn room_power_levels_event(
68		self,
69	) -> impl Future<Output = Result<Option<RoomPowerLevelsEvent<Self::Pdu>>>> + Send {
70		self.get(StateEventType::RoomPowerLevels, "".into())
71			.map_err(auth_input_error)
72			.map_ok(RoomPowerLevelsEvent::new)
73			.map(NotFound::optional)
74	}
75
76	/// Reads the current room join rule.
77	///
78	/// Missing join-rule state is an authorization input error.
79	fn join_rule(self) -> impl Future<Output = Result<JoinRule>> + Send {
80		self.get(StateEventType::RoomJoinRules, "".into())
81			.map_err(auth_input_error)
82			.map_ok(RoomJoinRulesEvent::new)
83			.map_err(required("m.room.join_rules"))
84			.and_then(|event| ready(event.join_rule().map_err(auth_input_error)))
85	}
86
87	/// Reads the invitation matching a third-party token.
88	///
89	/// Missing invitation state returns no event.
90	fn room_third_party_invite_event(
91		self,
92		token: &str,
93	) -> impl Future<Output = Result<Option<RoomThirdPartyInviteEvent<Self::Pdu>>>> + Send {
94		self.get(StateEventType::RoomThirdPartyInvite, token.into())
95			.map_err(auth_input_error)
96			.map_ok(RoomThirdPartyInviteEvent::new)
97			.map(NotFound::optional)
98	}
99}
100
101/// Promotes a missing required state event to an authorization input error.
102fn required(name: &'static str) -> impl Fn(Error) -> Error {
103	move |error| {
104		if error.is_not_found() {
105			err!("no `{name}` event in current state: {error}")
106		} else {
107			error
108		}
109	}
110}
111
112impl<'a> FetchState for &'a StateMap<PduEvent> {
113	type Pdu = &'a PduEvent;
114
115	fn get(
116		self,
117		ty: StateEventType,
118		key: StateKey,
119	) -> impl Future<Output = Result<Self::Pdu>> + Send {
120		ready(
121			BTreeMap::get(self, &(ty, key))
122				.ok_or_else(|| err!(Request(NotFound("Missing state event")))),
123		)
124	}
125}
126
127impl<'a> FetchState for &'a [(TypeStateKey, PduEvent)] {
128	type Pdu = &'a PduEvent;
129
130	fn get(
131		self,
132		ty: StateEventType,
133		key: StateKey,
134	) -> impl Future<Output = Result<Self::Pdu>> + Send {
135		ready(
136			self.iter()
137				.find(|((event_type, state_key), _)| *event_type == ty && *state_key == key)
138				.map(|(_, event)| event)
139				.ok_or_else(|| err!(Request(NotFound("Missing auth_event {ty:?},{key:?}")))),
140		)
141	}
142}