1use ruma::{
2 push::{
3 Action, PredefinedContentRuleId, PredefinedOverrideRuleId, RemovePushRuleError, RuleKind,
4 Ruleset,
5 },
6 RoomId,
7};
8
9use super::command::Command;
10use crate::NotificationSettingsError;
11
12#[derive(Clone, Debug)]
15pub(crate) struct RuleCommands {
16 pub(crate) commands: Vec<Command>,
17 pub(crate) rules: Ruleset,
18}
19
20impl RuleCommands {
21 pub(crate) fn new(rules: Ruleset) -> Self {
22 RuleCommands { commands: vec![], rules }
23 }
24
25 pub(crate) fn insert_rule(
27 &mut self,
28 kind: RuleKind,
29 room_id: &RoomId,
30 notify: bool,
31 ) -> Result<(), NotificationSettingsError> {
32 let command = match kind {
33 RuleKind::Room => Command::SetRoomPushRule { room_id: room_id.to_owned(), notify },
34 RuleKind::Override => Command::SetOverridePushRule {
35 rule_id: room_id.to_string(),
36 room_id: room_id.to_owned(),
37 notify,
38 },
39 _ => {
40 return Err(NotificationSettingsError::InvalidParameter(
41 "cannot insert a rule for this kind.".to_owned(),
42 ))
43 }
44 };
45
46 self.rules.insert(command.to_push_rule()?, None, None)?;
47 self.commands.push(command);
48
49 Ok(())
50 }
51
52 pub(crate) fn insert_keyword_rule(
54 &mut self,
55 keyword: String,
56 ) -> Result<(), NotificationSettingsError> {
57 let command = Command::SetKeywordPushRule { keyword };
58
59 self.rules.insert(command.to_push_rule()?, None, None)?;
60 self.commands.push(command);
61
62 Ok(())
63 }
64
65 pub(crate) fn delete_rule(
67 &mut self,
68 kind: RuleKind,
69 rule_id: String,
70 ) -> Result<(), RemovePushRuleError> {
71 self.rules.remove(kind.clone(), &rule_id)?;
72 self.commands.push(Command::DeletePushRule { kind, rule_id });
73
74 Ok(())
75 }
76
77 fn set_enabled_internal(
78 &mut self,
79 kind: RuleKind,
80 rule_id: &str,
81 enabled: bool,
82 ) -> Result<(), NotificationSettingsError> {
83 self.rules
84 .set_enabled(kind.clone(), rule_id, enabled)
85 .map_err(|_| NotificationSettingsError::RuleNotFound(rule_id.to_owned()))?;
86 self.commands.push(Command::SetPushRuleEnabled {
87 kind,
88 rule_id: rule_id.to_owned(),
89 enabled,
90 });
91 Ok(())
92 }
93
94 pub(crate) fn set_rule_enabled(
96 &mut self,
97 kind: RuleKind,
98 rule_id: &str,
99 enabled: bool,
100 ) -> Result<(), NotificationSettingsError> {
101 if rule_id == PredefinedOverrideRuleId::IsRoomMention.as_str() {
102 self.set_room_mention_enabled(enabled)
104 } else if rule_id == PredefinedOverrideRuleId::IsUserMention.as_str() {
105 self.set_user_mention_enabled(enabled)
107 } else {
108 self.set_enabled_internal(kind, rule_id, enabled)
109 }
110 }
111
112 fn set_user_mention_enabled(&mut self, enabled: bool) -> Result<(), NotificationSettingsError> {
114 self.set_enabled_internal(
117 RuleKind::Override,
118 PredefinedOverrideRuleId::IsUserMention.as_str(),
119 enabled,
120 )?;
121
122 #[allow(deprecated)]
125 {
126 self.set_enabled_internal(
128 RuleKind::Content,
129 PredefinedContentRuleId::ContainsUserName.as_str(),
130 enabled,
131 )?;
132
133 self.set_enabled_internal(
135 RuleKind::Override,
136 PredefinedOverrideRuleId::ContainsDisplayName.as_str(),
137 enabled,
138 )?;
139 }
140
141 Ok(())
142 }
143
144 fn set_room_mention_enabled(&mut self, enabled: bool) -> Result<(), NotificationSettingsError> {
146 self.set_enabled_internal(
149 RuleKind::Override,
150 PredefinedOverrideRuleId::IsRoomMention.as_str(),
151 enabled,
152 )?;
153
154 #[allow(deprecated)]
157 self.set_enabled_internal(
158 RuleKind::Override,
159 PredefinedOverrideRuleId::RoomNotif.as_str(),
160 enabled,
161 )?;
162
163 Ok(())
164 }
165
166 pub(crate) fn set_rule_actions(
169 &mut self,
170 kind: RuleKind,
171 rule_id: &str,
172 actions: Vec<Action>,
173 ) -> Result<(), NotificationSettingsError> {
174 self.rules
175 .set_actions(kind.clone(), rule_id, actions.clone())
176 .map_err(|_| NotificationSettingsError::RuleNotFound(rule_id.to_owned()))?;
177 self.commands.push(Command::SetPushRuleActions {
178 kind,
179 rule_id: rule_id.to_owned(),
180 actions,
181 });
182 Ok(())
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use assert_matches::assert_matches;
189 use matrix_sdk_test::async_test;
190 use ruma::{
191 push::{
192 Action, NewPushRule, NewSimplePushRule, PredefinedContentRuleId,
193 PredefinedOverrideRuleId, PredefinedUnderrideRuleId, RemovePushRuleError, RuleKind,
194 Ruleset, Tweak,
195 },
196 OwnedRoomId, RoomId, UserId,
197 };
198
199 use super::RuleCommands;
200 use crate::{error::NotificationSettingsError, notification_settings::command::Command};
201
202 fn get_server_default_ruleset() -> Ruleset {
203 let user_id = UserId::parse("@user:matrix.org").unwrap();
204 Ruleset::server_default(&user_id)
205 }
206
207 fn get_test_room_id() -> OwnedRoomId {
208 RoomId::parse("!AAAaAAAAAaaAAaaaaa:matrix.org").unwrap()
209 }
210
211 #[async_test]
212 async fn test_insert_rule_room() {
213 let room_id = get_test_room_id();
214 let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
215 rule_commands.insert_rule(RuleKind::Room, &room_id, true).unwrap();
216
217 assert!(rule_commands.rules.get(RuleKind::Room, &room_id).is_some());
219
220 assert_eq!(rule_commands.commands.len(), 1);
222 assert_matches!(&rule_commands.commands[0],
223 Command::SetRoomPushRule { room_id: command_room_id, notify } => {
224 assert_eq!(command_room_id, &room_id);
225 assert!(notify);
226 }
227 );
228 }
229
230 #[async_test]
231 async fn test_insert_rule_override() {
232 let room_id = get_test_room_id();
233 let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
234 rule_commands.insert_rule(RuleKind::Override, &room_id, true).unwrap();
235
236 assert!(rule_commands.rules.get(RuleKind::Override, &room_id).is_some());
238
239 assert_eq!(rule_commands.commands.len(), 1);
241 assert_matches!(&rule_commands.commands[0],
242 Command::SetOverridePushRule {room_id: command_room_id, rule_id, notify } => {
243 assert_eq!(command_room_id, &room_id);
244 assert_eq!(rule_id, room_id.as_str());
245 assert!(notify);
246 }
247 );
248 }
249
250 #[async_test]
251 async fn test_insert_rule_unsupported() {
252 let room_id = get_test_room_id();
253 let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
254
255 assert_matches!(
256 rule_commands.insert_rule(RuleKind::Underride, &room_id, true),
257 Err(NotificationSettingsError::InvalidParameter(_)) => {}
258 );
259
260 assert_matches!(
261 rule_commands.insert_rule(RuleKind::Content, &room_id, true),
262 Err(NotificationSettingsError::InvalidParameter(_)) => {}
263 );
264
265 assert_matches!(
266 rule_commands.insert_rule(RuleKind::Sender, &room_id, true),
267 Err(NotificationSettingsError::InvalidParameter(_)) => {}
268 );
269 }
270
271 #[async_test]
272 async fn test_delete_rule() {
273 let room_id = get_test_room_id();
274 let mut ruleset = get_server_default_ruleset();
275
276 let new_rule = NewSimplePushRule::new(room_id.to_owned(), vec![]);
277 ruleset.insert(NewPushRule::Room(new_rule), None, None).unwrap();
278
279 let mut rule_commands = RuleCommands::new(ruleset);
280
281 rule_commands.delete_rule(RuleKind::Room, room_id.to_string()).unwrap();
283
284 assert!(rule_commands.rules.get(RuleKind::Room, &room_id).is_none());
286
287 assert_eq!(rule_commands.commands.len(), 1);
289 assert_matches!(&rule_commands.commands[0],
290 Command::DeletePushRule { kind, rule_id } => {
291 assert_eq!(kind, &RuleKind::Room);
292 assert_eq!(rule_id, room_id.as_str());
293 }
294 );
295 }
296
297 #[async_test]
298 async fn test_delete_rule_errors() {
299 let room_id = get_test_room_id();
300 let ruleset = get_server_default_ruleset();
301
302 let mut rule_commands = RuleCommands::new(ruleset);
303
304 assert_matches!(
307 rule_commands.delete_rule(RuleKind::Room, room_id.to_string()),
308 Err(RemovePushRuleError::NotFound) => {}
309 );
310
311 assert_matches!(
313 rule_commands.delete_rule(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention.to_string()),
314 Err(RemovePushRuleError::ServerDefault) => {}
315 );
316
317 assert!(rule_commands.commands.is_empty());
318 }
319
320 #[async_test]
321 async fn test_set_rule_enabled() {
322 let mut ruleset = get_server_default_ruleset();
323
324 ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, false).unwrap();
326
327 let mut rule_commands = RuleCommands::new(ruleset);
328 rule_commands
329 .set_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str(), true)
330 .unwrap();
331
332 let rule = rule_commands
334 .rules
335 .get(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str())
336 .unwrap();
337 assert!(rule.enabled());
338
339 assert_eq!(rule_commands.commands.len(), 1);
341 assert_matches!(&rule_commands.commands[0],
342 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
343 assert_eq!(kind, &RuleKind::Override);
344 assert_eq!(rule_id, PredefinedOverrideRuleId::Reaction.as_str());
345 assert!(enabled);
346 }
347 );
348 }
349
350 #[async_test]
351 async fn test_set_rule_enabled_not_found() {
352 let ruleset = get_server_default_ruleset();
353 let mut rule_commands = RuleCommands::new(ruleset);
354 assert_eq!(
355 rule_commands.set_rule_enabled(RuleKind::Room, "unknown_rule_id", true),
356 Err(NotificationSettingsError::RuleNotFound("unknown_rule_id".to_owned()))
357 );
358 }
359
360 #[async_test]
361 async fn test_set_rule_enabled_user_mention() {
362 let mut ruleset = get_server_default_ruleset();
363 let mut rule_commands = RuleCommands::new(ruleset.clone());
364
365 ruleset
366 .set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention, false)
367 .unwrap();
368
369 #[allow(deprecated)]
370 {
371 ruleset
372 .set_enabled(
373 RuleKind::Override,
374 PredefinedOverrideRuleId::ContainsDisplayName,
375 false,
376 )
377 .unwrap();
378 ruleset
379 .set_enabled(RuleKind::Content, PredefinedContentRuleId::ContainsUserName, false)
380 .unwrap();
381 }
382
383 rule_commands
385 .set_rule_enabled(
386 RuleKind::Override,
387 PredefinedOverrideRuleId::IsUserMention.as_str(),
388 true,
389 )
390 .unwrap();
391
392 assert!(rule_commands
394 .rules
395 .get(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
396 .unwrap()
397 .enabled());
398 #[allow(deprecated)]
399 {
400 assert!(rule_commands
401 .rules
402 .get(RuleKind::Override, PredefinedOverrideRuleId::ContainsDisplayName)
403 .unwrap()
404 .enabled());
405 assert!(rule_commands
406 .rules
407 .get(RuleKind::Content, PredefinedContentRuleId::ContainsUserName)
408 .unwrap()
409 .enabled());
410 }
411
412 assert_eq!(rule_commands.commands.len(), 3);
414
415 assert_matches!(&rule_commands.commands[0],
416 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
417 assert_eq!(kind, &RuleKind::Override);
418 assert_eq!(rule_id, PredefinedOverrideRuleId::IsUserMention.as_str());
419 assert!(enabled);
420 }
421 );
422
423 #[allow(deprecated)]
424 {
425 assert_matches!(&rule_commands.commands[1],
426 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
427 assert_eq!(kind, &RuleKind::Content);
428 assert_eq!(rule_id, PredefinedContentRuleId::ContainsUserName.as_str());
429 assert!(enabled);
430 }
431 );
432
433 assert_matches!(&rule_commands.commands[2],
434 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
435 assert_eq!(kind, &RuleKind::Override);
436 assert_eq!(rule_id, PredefinedOverrideRuleId::ContainsDisplayName.as_str());
437 assert!(enabled);
438 }
439 );
440 }
441 }
442
443 #[async_test]
444 async fn test_set_rule_enabled_room_mention() {
445 let mut ruleset = get_server_default_ruleset();
446 let mut rule_commands = RuleCommands::new(ruleset.clone());
447
448 ruleset
449 .set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention, false)
450 .unwrap();
451
452 #[allow(deprecated)]
453 {
454 ruleset
455 .set_enabled(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif, false)
456 .unwrap();
457 }
458
459 rule_commands
460 .set_rule_enabled(
461 RuleKind::Override,
462 PredefinedOverrideRuleId::IsRoomMention.as_str(),
463 true,
464 )
465 .unwrap();
466
467 assert!(rule_commands
469 .rules
470 .get(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
471 .unwrap()
472 .enabled());
473 #[allow(deprecated)]
474 {
475 assert!(rule_commands
476 .rules
477 .get(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif)
478 .unwrap()
479 .enabled());
480 }
481
482 assert_eq!(rule_commands.commands.len(), 2);
484
485 assert_matches!(&rule_commands.commands[0],
486 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
487 assert_eq!(kind, &RuleKind::Override);
488 assert_eq!(rule_id, PredefinedOverrideRuleId::IsRoomMention.as_str());
489 assert!(enabled);
490 }
491 );
492
493 #[allow(deprecated)]
494 {
495 assert_matches!(&rule_commands.commands[1],
496 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
497 assert_eq!(kind, &RuleKind::Override);
498 assert_eq!(rule_id, PredefinedOverrideRuleId::RoomNotif.as_str());
499 assert!(enabled);
500 }
501 );
502 }
503 }
504
505 #[async_test]
506 async fn test_set_rule_actions() {
507 let mut ruleset = get_server_default_ruleset();
508 let mut rule_commands = RuleCommands::new(ruleset.clone());
509
510 ruleset
512 .set_actions(RuleKind::Underride, PredefinedUnderrideRuleId::Message, vec![])
513 .unwrap();
514
515 rule_commands
517 .set_rule_actions(
518 RuleKind::Underride,
519 PredefinedUnderrideRuleId::Message.as_str(),
520 vec![Action::Notify, Action::SetTweak(Tweak::Sound("default".into()))],
521 )
522 .unwrap();
523
524 let actions = rule_commands
526 .rules
527 .get(RuleKind::Underride, PredefinedUnderrideRuleId::Message)
528 .unwrap()
529 .actions();
530 assert_eq!(actions.len(), 2);
531
532 assert_eq!(rule_commands.commands.len(), 1);
534 assert_matches!(&rule_commands.commands[0],
535 Command::SetPushRuleActions { kind, rule_id, actions } => {
536 assert_eq!(kind, &RuleKind::Underride);
537 assert_eq!(rule_id, PredefinedUnderrideRuleId::Message.as_str());
538 assert_eq!(actions.len(), 2);
539 assert_matches!(&actions[0], Action::Notify);
540 assert_matches!(&actions[1], Action::SetTweak(Tweak::Sound(sound)) => {
541 assert_eq!(sound, "default");
542 });
543 }
544 );
545 }
546}