Skip to main content

tuwunel_matrix/room/state/resolution/events/
power_levels.rs

1//! Types to deserialize `m.room.power_levels` events.
2
3use std::ops::Deref;
4
5use ruma::{
6	Int, OwnedUserId, UserId,
7	events::{TimelineEventType, room::power_levels::UserPowerLevel},
8	int,
9	room_version_rules::AuthorizationRules,
10	serde::{
11		DebugAsRefStr, DisplayAsRefStr, JsonObject, OrdAsRefStr, deserialize_v1_powerlevel,
12		from_raw_json_value, vec_deserialize_int_powerlevel_values,
13		vec_deserialize_v1_powerlevel_values,
14	},
15};
16use serde::de::DeserializeOwned;
17use serde_json::{Error, from_value as from_json_value};
18use tuwunel_core::{Result, err, is_equal_to, ref_at};
19
20use crate::Event;
21
22/// The default value of the creator's power level.
23const DEFAULT_CREATOR_POWER_LEVEL: i32 = 100;
24
25/// A helper type for an [`Event`] of type `m.room.power_levels`.
26#[derive(Clone, Debug)]
27pub struct RoomPowerLevelsEvent<E: Event>(E);
28
29impl<E: Event> RoomPowerLevelsEvent<E> {
30	/// Construct a new `RoomPowerLevelsEvent` around the given event.
31	#[inline]
32	pub fn new(event: E) -> Self { Self(event) }
33
34	/// The deserialized content of the event.
35	fn deserialized_content(&self) -> Result<JsonObject> {
36		from_raw_json_value(self.content()).map_err(|error: Error| {
37			err!(Request(InvalidParam("malformed `m.room.power_levels` content: {error}")))
38		})
39	}
40
41	/// Get the value of a field that should contain an integer, if any.
42	///
43	/// The deserialization of this field is cached in memory.
44	pub(crate) fn get_as_int(
45		&self,
46		field: RoomPowerLevelsIntField,
47		rules: &AuthorizationRules,
48	) -> Result<Option<Int>> {
49		let content = self.deserialized_content()?;
50
51		let Some(value) = content.get(field.as_str()) else {
52			return Ok(None);
53		};
54
55		let res = if rules.integer_power_levels {
56			from_json_value(value.clone())
57		} else {
58			deserialize_v1_powerlevel(value)
59		};
60
61		let power_level = res.map(Some).map_err(|error| {
62			err!(Request(InvalidParam(
63				"unexpected format of `{field}` field in `content` of `m.room.power_levels` \
64				 event: {error}"
65			)))
66		})?;
67
68		Ok(power_level)
69	}
70
71	/// Get the value of a field that should contain an integer, or its default
72	/// value if it is absent.
73	#[inline]
74	pub(crate) fn get_as_int_or_default(
75		&self,
76		field: RoomPowerLevelsIntField,
77		rules: &AuthorizationRules,
78	) -> Result<Int> {
79		Ok(self
80			.get_as_int(field, rules)?
81			.unwrap_or_else(|| field.default_value()))
82	}
83
84	/// Get the value of a field that should contain a map of any value to
85	/// integer, if any.
86	fn get_as_int_map<T: Ord + DeserializeOwned>(
87		&self,
88		field: &str,
89		rules: &AuthorizationRules,
90	) -> Result<Option<Vec<(T, Int)>>> {
91		let content = self.deserialized_content()?;
92
93		let Some(value) = content.get(field) else {
94			return Ok(None);
95		};
96
97		let res = if rules.integer_power_levels {
98			vec_deserialize_int_powerlevel_values(value)
99		} else {
100			vec_deserialize_v1_powerlevel_values(value)
101		};
102
103		res.map(Some).map_err(|error| {
104			err!(Request(InvalidParam(
105				"unexpected format of `{field}` field in `content` of `m.room.power_levels` \
106				 event: {error}"
107			)))
108		})
109	}
110
111	/// Get the power levels required to send events, if any.
112	#[inline]
113	pub(crate) fn events(
114		&self,
115		rules: &AuthorizationRules,
116	) -> Result<Option<Vec<(TimelineEventType, Int)>>> {
117		self.get_as_int_map("events", rules)
118	}
119
120	/// Get the power levels required to trigger notifications, if any.
121	#[inline]
122	pub(crate) fn notifications(
123		&self,
124		rules: &AuthorizationRules,
125	) -> Result<Option<Vec<(String, Int)>>> {
126		self.get_as_int_map("notifications", rules)
127	}
128
129	/// Get the power levels of the users, if any.
130	///
131	/// The deserialization of this field is cached in memory.
132	#[inline]
133	pub(crate) fn users(
134		&self,
135		rules: &AuthorizationRules,
136	) -> Result<Option<Vec<(OwnedUserId, Int)>>> {
137		self.get_as_int_map("users", rules)
138	}
139
140	/// Get the power level of the user with the given ID.
141	///
142	/// Calling this method several times should be cheap because the necessary
143	/// deserialization results are cached.
144	pub(crate) fn user_power_level(
145		&self,
146		user_id: &UserId,
147		rules: &AuthorizationRules,
148	) -> Result<UserPowerLevel> {
149		self.users(rules)?
150			.as_ref()
151			.and_then(|users| get_value(users, user_id))
152			.copied()
153			.map_or_else(
154				|| self.get_as_int_or_default(RoomPowerLevelsIntField::UsersDefault, rules),
155				Ok,
156			)
157			.map(Into::into)
158	}
159
160	/// Get the power level required to send an event of the given type.
161	pub(crate) fn event_power_level(
162		&self,
163		event_type: &TimelineEventType,
164		state_key: Option<&str>,
165		rules: &AuthorizationRules,
166	) -> Result<Int> {
167		let events = self.events(rules)?;
168
169		if let Some(power_level) = events
170			.as_ref()
171			.and_then(|events| get_value(events, event_type))
172		{
173			return Ok(*power_level);
174		}
175
176		let default_field = if state_key.is_some() {
177			RoomPowerLevelsIntField::StateDefault
178		} else {
179			RoomPowerLevelsIntField::EventsDefault
180		};
181
182		self.get_as_int_or_default(default_field, rules)
183	}
184
185	/// Get a map of all the fields with an integer value in the `content` of an
186	/// `m.room.power_levels` event.
187	pub(crate) fn int_fields_map(
188		&self,
189		rules: &AuthorizationRules,
190	) -> Result<Vec<(RoomPowerLevelsIntField, Int)>> {
191		RoomPowerLevelsIntField::ALL
192			.iter()
193			.copied()
194			.filter_map(|field| match self.get_as_int(field, rules) {
195				| Ok(value) => value.map(|value| Ok((field, value))),
196				| Err(error) => Some(Err(error)),
197			})
198			.collect()
199	}
200}
201
202impl<E: Event> Deref for RoomPowerLevelsEvent<E> {
203	type Target = E;
204
205	#[inline]
206	fn deref(&self) -> &Self::Target { &self.0 }
207}
208
209/// Helper trait for `Option<RoomPowerLevelsEvent<E>>`.
210pub(crate) trait RoomPowerLevelsEventOptionExt {
211	/// Get the power level of the user with the given ID.
212	fn user_power_level(
213		&self,
214		user_id: &UserId,
215		creators: impl Iterator<Item = OwnedUserId>,
216		rules: &AuthorizationRules,
217	) -> Result<UserPowerLevel>;
218
219	/// Get the value of a field that should contain an integer, or its default
220	/// value if it is absent.
221	fn get_as_int_or_default(
222		&self,
223		field: RoomPowerLevelsIntField,
224		rules: &AuthorizationRules,
225	) -> Result<Int>;
226
227	/// Get the power level required to send an event of the given type.
228	fn event_power_level(
229		&self,
230		event_type: &TimelineEventType,
231		state_key: Option<&str>,
232		rules: &AuthorizationRules,
233	) -> Result<Int>;
234}
235
236impl<E> RoomPowerLevelsEventOptionExt for Option<RoomPowerLevelsEvent<E>>
237where
238	E: Event,
239{
240	fn user_power_level(
241		&self,
242		user_id: &UserId,
243		mut creators: impl Iterator<Item = OwnedUserId>,
244		rules: &AuthorizationRules,
245	) -> Result<UserPowerLevel> {
246		if rules.explicitly_privilege_room_creators && creators.any(is_equal_to!(user_id)) {
247			Ok(UserPowerLevel::Infinite)
248		} else if let Some(room_power_levels_event) = self {
249			room_power_levels_event.user_power_level(user_id, rules)
250		} else {
251			let power_level = if creators.any(is_equal_to!(user_id)) {
252				DEFAULT_CREATOR_POWER_LEVEL.into()
253			} else {
254				RoomPowerLevelsIntField::UsersDefault.default_value()
255			};
256
257			Ok(power_level.into())
258		}
259	}
260
261	fn get_as_int_or_default(
262		&self,
263		field: RoomPowerLevelsIntField,
264		rules: &AuthorizationRules,
265	) -> Result<Int> {
266		self.as_ref().map_or_else(
267			|| Ok(field.default_value()),
268			|room_power_levels_event| room_power_levels_event.get_as_int_or_default(field, rules),
269		)
270	}
271
272	fn event_power_level(
273		&self,
274		event_type: &TimelineEventType,
275		state_key: Option<&str>,
276		rules: &AuthorizationRules,
277	) -> Result<Int> {
278		self.as_ref().map_or_else(
279			|| {
280				let default_field = state_key
281					.is_some()
282					.then_some(RoomPowerLevelsIntField::StateDefault)
283					.unwrap_or(RoomPowerLevelsIntField::EventsDefault);
284
285				Ok(default_field.default_value())
286			},
287			|room_power_levels_event| {
288				room_power_levels_event.event_power_level(event_type, state_key, rules)
289			},
290		)
291	}
292}
293
294#[inline]
295pub(crate) fn get_value<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> Option<&'a V>
296where
297	&'a K: PartialEq<&'a B>,
298	B: ?Sized,
299{
300	position(vec, key)
301		.and_then(|i| vec.get(i))
302		.map(ref_at!(1))
303}
304
305#[inline]
306pub(crate) fn contains_key<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> bool
307where
308	&'a K: PartialEq<&'a B>,
309	B: ?Sized,
310{
311	position(vec, key).is_some()
312}
313
314fn position<'a, K, V, B>(vec: &'a [(K, V)], key: &'a B) -> Option<usize>
315where
316	&'a K: PartialEq<&'a B>,
317	B: ?Sized,
318{
319	vec.iter()
320		.map(ref_at!(0))
321		.position(is_equal_to!(key))
322}
323
324/// Fields in the `content` of an `m.room.power_levels` event with an integer
325/// value.
326#[derive(DebugAsRefStr, Clone, Copy, DisplayAsRefStr, PartialEq, Eq, OrdAsRefStr)]
327#[non_exhaustive]
328pub enum RoomPowerLevelsIntField {
329	/// `users_default`
330	UsersDefault,
331
332	/// `events_default`
333	EventsDefault,
334
335	/// `state_default`
336	StateDefault,
337
338	/// `ban`
339	Ban,
340
341	/// `redact`
342	Redact,
343
344	/// `kick`
345	Kick,
346
347	/// `invite`
348	Invite,
349}
350
351impl RoomPowerLevelsIntField {
352	/// A slice containing all the variants.
353	pub const ALL: &[Self] = &[
354		Self::UsersDefault,
355		Self::EventsDefault,
356		Self::StateDefault,
357		Self::Ban,
358		Self::Redact,
359		Self::Kick,
360		Self::Invite,
361	];
362
363	/// The string representation of this field.
364	#[inline]
365	#[must_use]
366	pub fn as_str(&self) -> &str { self.as_ref() }
367
368	/// The default value for this field if it is absent.
369	#[inline]
370	#[must_use]
371	pub fn default_value(self) -> Int {
372		match self {
373			| Self::UsersDefault | Self::EventsDefault | Self::Invite => int!(0),
374			| Self::StateDefault | Self::Kick | Self::Ban | Self::Redact => int!(50),
375		}
376	}
377}
378
379impl AsRef<str> for RoomPowerLevelsIntField {
380	#[inline]
381	fn as_ref(&self) -> &'static str {
382		match self {
383			| Self::UsersDefault => "users_default",
384			| Self::EventsDefault => "events_default",
385			| Self::StateDefault => "state_default",
386			| Self::Ban => "ban",
387			| Self::Redact => "redact",
388			| Self::Kick => "kick",
389			| Self::Invite => "invite",
390		}
391	}
392}