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