1use ruma::{
2 RoomId,
3 push::{
4 Action, NewPushRule, PredefinedContentRuleId, PredefinedOverrideRuleId,
5 RemovePushRuleError, RuleKind, Ruleset,
6 },
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 insert_custom_rule(
67 &mut self,
68 rule: NewPushRule,
69 ) -> Result<(), NotificationSettingsError> {
70 let command = Command::SetCustomPushRule { rule: rule.clone() };
71
72 self.rules.insert(rule, None, None)?;
73 self.commands.push(command);
74
75 Ok(())
76 }
77
78 pub(crate) fn delete_rule(
80 &mut self,
81 kind: RuleKind,
82 rule_id: String,
83 ) -> Result<(), RemovePushRuleError> {
84 self.rules.remove(kind.clone(), &rule_id)?;
85 self.commands.push(Command::DeletePushRule { kind, rule_id });
86
87 Ok(())
88 }
89
90 fn set_enabled_internal(
94 &mut self,
95 kind: RuleKind,
96 rule_id: &str,
97 enabled: bool,
98 ) -> Result<(), NotificationSettingsError> {
99 self.rules
100 .set_enabled(kind.clone(), rule_id, enabled)
101 .map_err(|_| NotificationSettingsError::RuleNotFound(rule_id.to_owned()))?;
102 self.commands.push(Command::SetPushRuleEnabled {
103 kind,
104 rule_id: rule_id.to_owned(),
105 enabled,
106 });
107 Ok(())
108 }
109
110 pub(crate) fn set_rule_enabled(
112 &mut self,
113 kind: RuleKind,
114 rule_id: &str,
115 enabled: bool,
116 ) -> Result<(), NotificationSettingsError> {
117 if rule_id == PredefinedOverrideRuleId::IsRoomMention.as_str() {
118 self.set_room_mention_enabled(enabled)
120 } else if rule_id == PredefinedOverrideRuleId::IsUserMention.as_str() {
121 self.set_user_mention_enabled(enabled)
123 } else {
124 self.set_enabled_internal(kind, rule_id, enabled)
125 }
126 }
127
128 fn set_user_mention_enabled(&mut self, enabled: bool) -> Result<(), NotificationSettingsError> {
130 self.set_enabled_internal(
133 RuleKind::Override,
134 PredefinedOverrideRuleId::IsUserMention.as_str(),
135 enabled,
136 )?;
137
138 #[allow(deprecated)]
141 {
142 if let Err(err) = self.set_enabled_internal(
144 RuleKind::Content,
145 PredefinedContentRuleId::ContainsUserName.as_str(),
146 enabled,
147 ) {
148 if !err.is_rule_not_found() {
150 return Err(err);
151 }
152 }
153
154 if let Err(err) = self.set_enabled_internal(
156 RuleKind::Override,
157 PredefinedOverrideRuleId::ContainsDisplayName.as_str(),
158 enabled,
159 ) {
160 if !err.is_rule_not_found() {
162 return Err(err);
163 }
164 }
165 }
166
167 Ok(())
168 }
169
170 fn set_room_mention_enabled(&mut self, enabled: bool) -> Result<(), NotificationSettingsError> {
172 self.set_enabled_internal(
175 RuleKind::Override,
176 PredefinedOverrideRuleId::IsRoomMention.as_str(),
177 enabled,
178 )?;
179
180 #[allow(deprecated)]
183 if let Err(err) = self.set_enabled_internal(
184 RuleKind::Override,
185 PredefinedOverrideRuleId::RoomNotif.as_str(),
186 enabled,
187 ) {
188 if !err.is_rule_not_found() {
190 return Err(err);
191 }
192 }
193
194 Ok(())
195 }
196
197 pub(crate) fn set_rule_actions(
200 &mut self,
201 kind: RuleKind,
202 rule_id: &str,
203 actions: Vec<Action>,
204 ) -> Result<(), NotificationSettingsError> {
205 self.rules
206 .set_actions(kind.clone(), rule_id, actions.clone())
207 .map_err(|_| NotificationSettingsError::RuleNotFound(rule_id.to_owned()))?;
208 self.commands.push(Command::SetPushRuleActions {
209 kind,
210 rule_id: rule_id.to_owned(),
211 actions,
212 });
213 Ok(())
214 }
215}
216
217#[cfg(test)]
218mod tests {
219 use assert_matches::assert_matches;
220 use matrix_sdk_test::{
221 async_test,
222 notification_settings::{
223 get_server_default_ruleset, server_default_ruleset_with_legacy_mentions,
224 },
225 };
226 use ruma::{
227 OwnedRoomId, RoomId,
228 push::{
229 Action, NewPushRule, NewSimplePushRule, PredefinedContentRuleId,
230 PredefinedOverrideRuleId, PredefinedUnderrideRuleId, RemovePushRuleError, RuleKind,
231 Tweak,
232 },
233 };
234
235 use super::RuleCommands;
236 use crate::{error::NotificationSettingsError, notification_settings::command::Command};
237
238 fn get_test_room_id() -> OwnedRoomId {
239 RoomId::parse("!AAAaAAAAAaaAAaaaaa:matrix.org").unwrap()
240 }
241
242 #[async_test]
243 async fn test_insert_rule_room() {
244 let room_id = get_test_room_id();
245 let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
246 rule_commands.insert_rule(RuleKind::Room, &room_id, true).unwrap();
247
248 assert!(rule_commands.rules.get(RuleKind::Room, &room_id).is_some());
250
251 assert_eq!(rule_commands.commands.len(), 1);
253 assert_matches!(&rule_commands.commands[0],
254 Command::SetRoomPushRule { room_id: command_room_id, notify } => {
255 assert_eq!(command_room_id, &room_id);
256 assert!(notify);
257 }
258 );
259 }
260
261 #[async_test]
262 async fn test_insert_rule_override() {
263 let room_id = get_test_room_id();
264 let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
265 rule_commands.insert_rule(RuleKind::Override, &room_id, true).unwrap();
266
267 assert!(rule_commands.rules.get(RuleKind::Override, &room_id).is_some());
269
270 assert_eq!(rule_commands.commands.len(), 1);
272 assert_matches!(&rule_commands.commands[0],
273 Command::SetOverridePushRule {room_id: command_room_id, rule_id, notify } => {
274 assert_eq!(command_room_id, &room_id);
275 assert_eq!(rule_id, room_id.as_str());
276 assert!(notify);
277 }
278 );
279 }
280
281 #[async_test]
282 async fn test_insert_rule_unsupported() {
283 let room_id = get_test_room_id();
284 let mut rule_commands = RuleCommands::new(get_server_default_ruleset());
285
286 assert_matches!(
287 rule_commands.insert_rule(RuleKind::Underride, &room_id, true),
288 Err(NotificationSettingsError::InvalidParameter(_)) => {}
289 );
290
291 assert_matches!(
292 rule_commands.insert_rule(RuleKind::Content, &room_id, true),
293 Err(NotificationSettingsError::InvalidParameter(_)) => {}
294 );
295
296 assert_matches!(
297 rule_commands.insert_rule(RuleKind::Sender, &room_id, true),
298 Err(NotificationSettingsError::InvalidParameter(_)) => {}
299 );
300 }
301
302 #[async_test]
303 async fn test_delete_rule() {
304 let room_id = get_test_room_id();
305 let mut ruleset = get_server_default_ruleset();
306
307 let new_rule = NewSimplePushRule::new(room_id.to_owned(), vec![]);
308 ruleset.insert(NewPushRule::Room(new_rule), None, None).unwrap();
309
310 let mut rule_commands = RuleCommands::new(ruleset);
311
312 rule_commands.delete_rule(RuleKind::Room, room_id.to_string()).unwrap();
314
315 assert!(rule_commands.rules.get(RuleKind::Room, &room_id).is_none());
317
318 assert_eq!(rule_commands.commands.len(), 1);
320 assert_matches!(&rule_commands.commands[0],
321 Command::DeletePushRule { kind, rule_id } => {
322 assert_eq!(kind, &RuleKind::Room);
323 assert_eq!(rule_id, room_id.as_str());
324 }
325 );
326 }
327
328 #[async_test]
329 async fn test_delete_rule_errors() {
330 let room_id = get_test_room_id();
331 let ruleset = get_server_default_ruleset();
332
333 let mut rule_commands = RuleCommands::new(ruleset);
334
335 assert_matches!(
338 rule_commands.delete_rule(RuleKind::Room, room_id.to_string()),
339 Err(RemovePushRuleError::NotFound) => {}
340 );
341
342 assert_matches!(
344 rule_commands.delete_rule(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention.to_string()),
345 Err(RemovePushRuleError::ServerDefault) => {}
346 );
347
348 assert!(rule_commands.commands.is_empty());
349 }
350
351 #[async_test]
352 async fn test_set_rule_enabled() {
353 let mut ruleset = get_server_default_ruleset();
354
355 ruleset.set_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction, false).unwrap();
357
358 let mut rule_commands = RuleCommands::new(ruleset);
359 rule_commands
360 .set_rule_enabled(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str(), true)
361 .unwrap();
362
363 let rule = rule_commands
365 .rules
366 .get(RuleKind::Override, PredefinedOverrideRuleId::Reaction.as_str())
367 .unwrap();
368 assert!(rule.enabled());
369
370 assert_eq!(rule_commands.commands.len(), 1);
372 assert_matches!(&rule_commands.commands[0],
373 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
374 assert_eq!(kind, &RuleKind::Override);
375 assert_eq!(rule_id, PredefinedOverrideRuleId::Reaction.as_str());
376 assert!(enabled);
377 }
378 );
379 }
380
381 #[async_test]
382 async fn test_set_rule_enabled_not_found() {
383 let ruleset = get_server_default_ruleset();
384 let mut rule_commands = RuleCommands::new(ruleset);
385 assert_eq!(
386 rule_commands.set_rule_enabled(RuleKind::Room, "unknown_rule_id", true),
387 Err(NotificationSettingsError::RuleNotFound("unknown_rule_id".to_owned()))
388 );
389 }
390
391 #[async_test]
392 async fn test_set_rule_enabled_user_mention() {
393 let mut ruleset = server_default_ruleset_with_legacy_mentions();
394 let mut rule_commands = RuleCommands::new(ruleset.clone());
395
396 ruleset
397 .set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention, false)
398 .unwrap();
399
400 #[allow(deprecated)]
401 {
402 ruleset
403 .set_enabled(
404 RuleKind::Override,
405 PredefinedOverrideRuleId::ContainsDisplayName,
406 false,
407 )
408 .unwrap();
409 ruleset
410 .set_enabled(RuleKind::Content, PredefinedContentRuleId::ContainsUserName, false)
411 .unwrap();
412 }
413
414 rule_commands
416 .set_rule_enabled(
417 RuleKind::Override,
418 PredefinedOverrideRuleId::IsUserMention.as_str(),
419 true,
420 )
421 .unwrap();
422
423 assert!(
425 rule_commands
426 .rules
427 .get(RuleKind::Override, PredefinedOverrideRuleId::IsUserMention)
428 .unwrap()
429 .enabled()
430 );
431 #[allow(deprecated)]
432 {
433 assert!(
434 rule_commands
435 .rules
436 .get(RuleKind::Override, PredefinedOverrideRuleId::ContainsDisplayName)
437 .unwrap()
438 .enabled()
439 );
440 assert!(
441 rule_commands
442 .rules
443 .get(RuleKind::Content, PredefinedContentRuleId::ContainsUserName)
444 .unwrap()
445 .enabled()
446 );
447 }
448
449 assert_eq!(rule_commands.commands.len(), 3);
451
452 assert_matches!(&rule_commands.commands[0],
453 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
454 assert_eq!(kind, &RuleKind::Override);
455 assert_eq!(rule_id, PredefinedOverrideRuleId::IsUserMention.as_str());
456 assert!(enabled);
457 }
458 );
459
460 #[allow(deprecated)]
461 {
462 assert_matches!(&rule_commands.commands[1],
463 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
464 assert_eq!(kind, &RuleKind::Content);
465 assert_eq!(rule_id, PredefinedContentRuleId::ContainsUserName.as_str());
466 assert!(enabled);
467 }
468 );
469
470 assert_matches!(&rule_commands.commands[2],
471 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
472 assert_eq!(kind, &RuleKind::Override);
473 assert_eq!(rule_id, PredefinedOverrideRuleId::ContainsDisplayName.as_str());
474 assert!(enabled);
475 }
476 );
477 }
478 }
479
480 #[async_test]
481 async fn test_set_rule_enabled_room_mention() {
482 let mut ruleset = server_default_ruleset_with_legacy_mentions();
483 let mut rule_commands = RuleCommands::new(ruleset.clone());
484
485 ruleset
486 .set_enabled(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention, false)
487 .unwrap();
488
489 #[allow(deprecated)]
490 {
491 ruleset
492 .set_enabled(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif, false)
493 .unwrap();
494 }
495
496 rule_commands
497 .set_rule_enabled(
498 RuleKind::Override,
499 PredefinedOverrideRuleId::IsRoomMention.as_str(),
500 true,
501 )
502 .unwrap();
503
504 assert!(
506 rule_commands
507 .rules
508 .get(RuleKind::Override, PredefinedOverrideRuleId::IsRoomMention)
509 .unwrap()
510 .enabled()
511 );
512 #[allow(deprecated)]
513 {
514 assert!(
515 rule_commands
516 .rules
517 .get(RuleKind::Override, PredefinedOverrideRuleId::RoomNotif)
518 .unwrap()
519 .enabled()
520 );
521 }
522
523 assert_eq!(rule_commands.commands.len(), 2);
525
526 assert_matches!(&rule_commands.commands[0],
527 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
528 assert_eq!(kind, &RuleKind::Override);
529 assert_eq!(rule_id, PredefinedOverrideRuleId::IsRoomMention.as_str());
530 assert!(enabled);
531 }
532 );
533
534 #[allow(deprecated)]
535 {
536 assert_matches!(&rule_commands.commands[1],
537 Command::SetPushRuleEnabled { kind, rule_id, enabled } => {
538 assert_eq!(kind, &RuleKind::Override);
539 assert_eq!(rule_id, PredefinedOverrideRuleId::RoomNotif.as_str());
540 assert!(enabled);
541 }
542 );
543 }
544 }
545
546 #[async_test]
547 async fn test_set_rule_actions() {
548 let mut ruleset = get_server_default_ruleset();
549 let mut rule_commands = RuleCommands::new(ruleset.clone());
550
551 ruleset
553 .set_actions(RuleKind::Underride, PredefinedUnderrideRuleId::Message, vec![])
554 .unwrap();
555
556 rule_commands
558 .set_rule_actions(
559 RuleKind::Underride,
560 PredefinedUnderrideRuleId::Message.as_str(),
561 vec![Action::Notify, Action::SetTweak(Tweak::Sound("default".into()))],
562 )
563 .unwrap();
564
565 let actions = rule_commands
567 .rules
568 .get(RuleKind::Underride, PredefinedUnderrideRuleId::Message)
569 .unwrap()
570 .actions();
571 assert_eq!(actions.len(), 2);
572
573 assert_eq!(rule_commands.commands.len(), 1);
575 assert_matches!(&rule_commands.commands[0],
576 Command::SetPushRuleActions { kind, rule_id, actions } => {
577 assert_eq!(kind, &RuleKind::Underride);
578 assert_eq!(rule_id, PredefinedUnderrideRuleId::Message.as_str());
579 assert_eq!(actions.len(), 2);
580 assert_matches!(&actions[0], Action::Notify);
581 assert_matches!(&actions[1], Action::SetTweak(Tweak::Sound(sound)) => {
582 assert_eq!(sound, "default");
583 });
584 }
585 );
586 }
587}