tuwunel_matrix/room/state/resolution/events/
power_levels.rs1use 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
22const DEFAULT_CREATOR_POWER_LEVEL: i32 = 100;
24
25#[derive(Clone, Debug)]
27pub struct RoomPowerLevelsEvent<E: Event>(E);
28
29impl<E: Event> RoomPowerLevelsEvent<E> {
30 #[inline]
32 pub fn new(event: E) -> Self { Self(event) }
33
34 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 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 #[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 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 #[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 #[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 #[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 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 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 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
209pub(crate) trait RoomPowerLevelsEventOptionExt {
211 fn user_power_level(
213 &self,
214 user_id: &UserId,
215 creators: impl Iterator<Item = OwnedUserId>,
216 rules: &AuthorizationRules,
217 ) -> Result<UserPowerLevel>;
218
219 fn get_as_int_or_default(
222 &self,
223 field: RoomPowerLevelsIntField,
224 rules: &AuthorizationRules,
225 ) -> Result<Int>;
226
227 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#[derive(DebugAsRefStr, Clone, Copy, DisplayAsRefStr, PartialEq, Eq, OrdAsRefStr)]
327#[non_exhaustive]
328pub enum RoomPowerLevelsIntField {
329 UsersDefault,
331
332 EventsDefault,
334
335 StateDefault,
337
338 Ban,
340
341 Redact,
343
344 Kick,
346
347 Invite,
349}
350
351impl RoomPowerLevelsIntField {
352 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 #[inline]
365 #[must_use]
366 pub fn as_str(&self) -> &str { self.as_ref() }
367
368 #[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}