palpo_core/push/predefined.rs
1//! Constructors for [predefined push rules].
2//!
3//! [predefined push rules]: https://spec.matrix.org/latest/client-server-api/#predefined-rules
4
5use palpo_macros::StringEnum;
6
7use super::{Action, ConditionalPushRule, PushCondition::*, RoomMemberCountIs, RuleKind, Ruleset, Tweak};
8use crate::{PrivOwnedStr, UserId};
9
10impl Ruleset {
11 /// The list of all [predefined push rules].
12 ///
13 /// # Parameters
14 ///
15 /// - `user_id`: the user for which to generate the default rules. Some rules depend on the
16 /// user's ID (for instance those to send notifications when they are mentioned).
17 ///
18 /// [predefined push rules]: https://spec.matrix.org/latest/client-server-api/#predefined-rules
19 pub fn server_default(user_id: &UserId) -> Self {
20 Self {
21 content: Default::default(),
22 override_: [
23 ConditionalPushRule::master(),
24 ConditionalPushRule::suppress_notices(),
25 ConditionalPushRule::invite_for_me(user_id),
26 ConditionalPushRule::member_event(),
27 ConditionalPushRule::is_user_mention(user_id),
28 ConditionalPushRule::is_room_mention(),
29 ConditionalPushRule::tombstone(),
30 ConditionalPushRule::reaction(),
31 ConditionalPushRule::server_acl(),
32 ConditionalPushRule::suppress_edits(),
33 #[cfg(feature = "unstable-msc3930")]
34 ConditionalPushRule::poll_response(),
35 ]
36 .into(),
37 underride: [
38 ConditionalPushRule::call(),
39 ConditionalPushRule::encrypted_room_one_to_one(),
40 ConditionalPushRule::room_one_to_one(),
41 ConditionalPushRule::message(),
42 ConditionalPushRule::encrypted(),
43 #[cfg(feature = "unstable-msc3930")]
44 ConditionalPushRule::poll_start_one_to_one(),
45 #[cfg(feature = "unstable-msc3930")]
46 ConditionalPushRule::poll_start(),
47 #[cfg(feature = "unstable-msc3930")]
48 ConditionalPushRule::poll_end_one_to_one(),
49 #[cfg(feature = "unstable-msc3930")]
50 ConditionalPushRule::poll_end(),
51 ]
52 .into(),
53 ..Default::default()
54 }
55 }
56
57 /// Update this ruleset with the given server-default push rules.
58 ///
59 /// This will replace the server-default rules in this ruleset (with `default` set to `true`)
60 /// with the given ones while keeping the `enabled` and `actions` fields in the same state.
61 ///
62 /// The default rules in this ruleset that are not in the new server-default rules are removed.
63 ///
64 /// # Parameters
65 ///
66 /// - `server_default`: the new server-default push rules. This ruleset must not contain
67 /// non-default rules.
68 pub fn update_with_server_default(&mut self, mut new_server_default: Ruleset) {
69 // Copy the default rules states from the old rules to the new rules and remove the
70 // server-default rules from the old rules.
71 macro_rules! copy_rules_state {
72 ($new_ruleset:ident, $old_ruleset:ident, @fields $($field_name:ident),+) => {
73 $(
74 $new_ruleset.$field_name = $new_ruleset
75 .$field_name
76 .into_iter()
77 .map(|mut new_rule| {
78 if let Some(old_rule) =
79 $old_ruleset.$field_name.swap_take(new_rule.rule_id.as_str())
80 {
81 new_rule.enabled = old_rule.enabled;
82 new_rule.actions = old_rule.actions;
83 }
84
85 new_rule
86 })
87 .collect();
88 )+
89 };
90 }
91 copy_rules_state!(new_server_default, self, @fields override_, content, room, sender, underride);
92
93 // Remove the remaining server-default rules from the old rules.
94 macro_rules! remove_remaining_default_rules {
95 ($ruleset:ident, @fields $($field_name:ident),+) => {
96 $(
97 $ruleset.$field_name.retain(|rule| !rule.default);
98 )+
99 };
100 }
101 remove_remaining_default_rules!(self, @fields override_, content, room, sender, underride);
102
103 // `.m.rule.master` comes before all other push rules, while the other server-default push
104 // rules come after.
105 if let Some(master_rule) = new_server_default
106 .override_
107 .take(PredefinedOverrideRuleId::Master.as_str())
108 {
109 let (pos, _) = self.override_.insert_full(master_rule);
110 self.override_.move_index(pos, 0);
111 }
112
113 // Merge the new server-default rules into the old rules.
114 macro_rules! merge_rules {
115 ($old_ruleset:ident, $new_ruleset:ident, @fields $($field_name:ident),+) => {
116 $(
117 $old_ruleset.$field_name.extend($new_ruleset.$field_name);
118 )+
119 };
120 }
121 merge_rules!(self, new_server_default, @fields override_, content, room, sender, underride);
122 }
123}
124
125/// Default override push rules
126impl ConditionalPushRule {
127 /// Matches all events, this can be enabled to turn off all push notifications other than those
128 /// generated by override rules set by the user.
129 pub fn master() -> Self {
130 Self {
131 actions: vec![],
132 default: true,
133 enabled: false,
134 rule_id: PredefinedOverrideRuleId::Master.to_string(),
135 conditions: vec![],
136 }
137 }
138
139 /// Matches messages with a `msgtype` of `notice`.
140 pub fn suppress_notices() -> Self {
141 Self {
142 actions: vec![],
143 default: true,
144 enabled: true,
145 rule_id: PredefinedOverrideRuleId::SuppressNotices.to_string(),
146 conditions: vec![EventMatch {
147 key: "content.msgtype".into(),
148 pattern: "m.notice".into(),
149 }],
150 }
151 }
152
153 /// Matches any invites to a new room for this user.
154 pub fn invite_for_me(user_id: &UserId) -> Self {
155 Self {
156 actions: vec![
157 Action::Notify,
158 Action::SetTweak(Tweak::Sound("default".into())),
159 Action::SetTweak(Tweak::Highlight(false)),
160 ],
161 default: true,
162 enabled: true,
163 rule_id: PredefinedOverrideRuleId::InviteForMe.to_string(),
164 conditions: vec![
165 EventMatch {
166 key: "type".into(),
167 pattern: "m.room.member".into(),
168 },
169 EventMatch {
170 key: "content.membership".into(),
171 pattern: "invite".into(),
172 },
173 EventMatch {
174 key: "state_key".into(),
175 pattern: user_id.to_string(),
176 },
177 ],
178 }
179 }
180
181 /// Matches any `m.room.member_event`.
182 pub fn member_event() -> Self {
183 Self {
184 actions: vec![],
185 default: true,
186 enabled: true,
187 rule_id: PredefinedOverrideRuleId::MemberEvent.to_string(),
188 conditions: vec![EventMatch {
189 key: "type".into(),
190 pattern: "m.room.member".into(),
191 }],
192 }
193 }
194
195 /// Matches any message which contains the user’s Matrix ID in the list of `user_ids` under the
196 /// `m.mentions` property.
197 pub fn is_user_mention(user_id: &UserId) -> Self {
198 Self {
199 actions: vec![
200 Action::Notify,
201 Action::SetTweak(Tweak::Sound("default".to_owned())),
202 Action::SetTweak(Tweak::Highlight(true)),
203 ],
204 default: true,
205 enabled: true,
206 rule_id: PredefinedOverrideRuleId::IsUserMention.to_string(),
207 conditions: vec![EventPropertyContains {
208 key: r"content.m\.mentions.user_ids".to_owned(),
209 value: user_id.as_str().into(),
210 }],
211 }
212 }
213
214 /// Matches any state event whose type is `m.room.tombstone`. This
215 /// is intended to notify users of a room when it is upgraded,
216 /// similar to what an `@room` notification would accomplish.
217 pub fn tombstone() -> Self {
218 Self {
219 actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(true))],
220 default: true,
221 enabled: true,
222 rule_id: PredefinedOverrideRuleId::Tombstone.to_string(),
223 conditions: vec![
224 EventMatch {
225 key: "type".into(),
226 pattern: "m.room.tombstone".into(),
227 },
228 EventMatch {
229 key: "state_key".into(),
230 pattern: "".into(),
231 },
232 ],
233 }
234 }
235
236 /// Matches any message from a sender with the proper power level with the `room` property of
237 /// the `m.mentions` property set to `true`.
238 pub fn is_room_mention() -> Self {
239 Self {
240 actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(true))],
241 default: true,
242 enabled: true,
243 rule_id: PredefinedOverrideRuleId::IsRoomMention.to_string(),
244 conditions: vec![
245 EventPropertyIs {
246 key: r"content.m\.mentions.room".to_owned(),
247 value: true.into(),
248 },
249 SenderNotificationPermission { key: "room".to_owned() },
250 ],
251 }
252 }
253
254 /// Matches [reactions] to a message.
255 ///
256 /// [reactions]: https://spec.matrix.org/latest/client-server-api/#event-annotations-and-reactions
257 pub fn reaction() -> Self {
258 Self {
259 actions: vec![],
260 default: true,
261 enabled: true,
262 rule_id: PredefinedOverrideRuleId::Reaction.to_string(),
263 conditions: vec![EventMatch {
264 key: "type".into(),
265 pattern: "m.reaction".into(),
266 }],
267 }
268 }
269
270 /// Matches [room server ACLs].
271 ///
272 /// [room server ACLs]: https://spec.matrix.org/latest/client-server-api/#server-access-control-lists-acls-for-rooms
273 pub fn server_acl() -> Self {
274 Self {
275 actions: vec![],
276 default: true,
277 enabled: true,
278 rule_id: PredefinedOverrideRuleId::RoomServerAcl.to_string(),
279 conditions: vec![
280 EventMatch {
281 key: "type".into(),
282 pattern: "m.room.server_acl".into(),
283 },
284 EventMatch {
285 key: "state_key".into(),
286 pattern: "".into(),
287 },
288 ],
289 }
290 }
291
292 /// Matches [event replacements].
293 ///
294 /// [event replacements]: https://spec.matrix.org/latest/client-server-api/#event-replacements
295 pub fn suppress_edits() -> Self {
296 Self {
297 actions: vec![],
298 default: true,
299 enabled: true,
300 rule_id: PredefinedOverrideRuleId::SuppressEdits.to_string(),
301 conditions: vec![EventPropertyIs {
302 key: r"content.m\.relates_to.rel_type".to_owned(),
303 value: "m.replace".into(),
304 }],
305 }
306 }
307
308 /// Matches a poll response event sent in any room.
309 ///
310 /// This rule uses the unstable prefixes defined in [MSC3381] and [MSC3930].
311 ///
312 /// [MSC3381]: https://github.com/matrix-org/matrix-spec-proposals/pull/3381
313 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
314 #[cfg(feature = "unstable-msc3930")]
315 pub fn poll_response() -> Self {
316 Self {
317 rule_id: PredefinedOverrideRuleId::PollResponse.to_string(),
318 default: true,
319 enabled: true,
320 conditions: vec![EventPropertyIs {
321 key: "type".to_owned(),
322 value: "org.matrix.msc3381.poll.response".into(),
323 }],
324 actions: vec![],
325 }
326 }
327}
328
329/// Default underrides push rules
330impl ConditionalPushRule {
331 /// Matches any incoming VOIP call.
332 pub fn call() -> Self {
333 Self {
334 rule_id: PredefinedUnderrideRuleId::Call.to_string(),
335 default: true,
336 enabled: true,
337 conditions: vec![EventMatch {
338 key: "type".into(),
339 pattern: "m.call.invite".into(),
340 }],
341 actions: vec![
342 Action::Notify,
343 Action::SetTweak(Tweak::Sound("ring".into())),
344 Action::SetTweak(Tweak::Highlight(false)),
345 ],
346 }
347 }
348
349 /// Matches any encrypted event sent in a room with exactly two members.
350 ///
351 /// Unlike other push rules, this rule cannot be matched against the content of the event by
352 /// nature of it being encrypted. This causes the rule to be an "all or nothing" match where it
353 /// either matches all events that are encrypted (in 1:1 rooms) or none.
354 pub fn encrypted_room_one_to_one() -> Self {
355 Self {
356 rule_id: PredefinedUnderrideRuleId::EncryptedRoomOneToOne.to_string(),
357 default: true,
358 enabled: true,
359 conditions: vec![
360 RoomMemberCount {
361 is: RoomMemberCountIs::from(2),
362 },
363 EventMatch {
364 key: "type".into(),
365 pattern: "m.room.encrypted".into(),
366 },
367 ],
368 actions: vec![
369 Action::Notify,
370 Action::SetTweak(Tweak::Sound("default".into())),
371 Action::SetTweak(Tweak::Highlight(false)),
372 ],
373 }
374 }
375
376 /// Matches any message sent in a room with exactly two members.
377 pub fn room_one_to_one() -> Self {
378 Self {
379 rule_id: PredefinedUnderrideRuleId::RoomOneToOne.to_string(),
380 default: true,
381 enabled: true,
382 conditions: vec![
383 RoomMemberCount {
384 is: RoomMemberCountIs::from(2),
385 },
386 EventMatch {
387 key: "type".into(),
388 pattern: "m.room.message".into(),
389 },
390 ],
391 actions: vec![
392 Action::Notify,
393 Action::SetTweak(Tweak::Sound("default".into())),
394 Action::SetTweak(Tweak::Highlight(false)),
395 ],
396 }
397 }
398
399 /// Matches all chat messages.
400 pub fn message() -> Self {
401 Self {
402 rule_id: PredefinedUnderrideRuleId::Message.to_string(),
403 default: true,
404 enabled: true,
405 conditions: vec![EventMatch {
406 key: "type".into(),
407 pattern: "m.room.message".into(),
408 }],
409 actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(false))],
410 }
411 }
412
413 /// Matches all encrypted events.
414 ///
415 /// Unlike other push rules, this rule cannot be matched against the content of the event by
416 /// nature of it being encrypted. This causes the rule to be an "all or nothing" match where it
417 /// either matches all events that are encrypted (in group rooms) or none.
418 pub fn encrypted() -> Self {
419 Self {
420 rule_id: PredefinedUnderrideRuleId::Encrypted.to_string(),
421 default: true,
422 enabled: true,
423 conditions: vec![EventMatch {
424 key: "type".into(),
425 pattern: "m.room.encrypted".into(),
426 }],
427 actions: vec![Action::Notify, Action::SetTweak(Tweak::Highlight(false))],
428 }
429 }
430
431 /// Matches a poll start event sent in a room with exactly two members.
432 ///
433 /// This rule uses the unstable prefixes defined in [MSC3381] and [MSC3930].
434 ///
435 /// [MSC3381]: https://github.com/matrix-org/matrix-spec-proposals/pull/3381
436 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
437 #[cfg(feature = "unstable-msc3930")]
438 pub fn poll_start_one_to_one() -> Self {
439 Self {
440 rule_id: PredefinedUnderrideRuleId::PollStartOneToOne.to_string(),
441 default: true,
442 enabled: true,
443 conditions: vec![
444 RoomMemberCount {
445 is: RoomMemberCountIs::from(2),
446 },
447 EventPropertyIs {
448 key: "type".to_owned(),
449 value: "org.matrix.msc3381.poll.start".into(),
450 },
451 ],
452 actions: vec![Notify, SetTweak(Tweak::Sound("default".into()))],
453 }
454 }
455
456 /// Matches a poll start event sent in any room.
457 ///
458 /// This rule uses the unstable prefixes defined in [MSC3381] and [MSC3930].
459 ///
460 /// [MSC3381]: https://github.com/matrix-org/matrix-spec-proposals/pull/3381
461 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
462 #[cfg(feature = "unstable-msc3930")]
463 pub fn poll_start() -> Self {
464 Self {
465 rule_id: PredefinedUnderrideRuleId::PollStart.to_string(),
466 default: true,
467 enabled: true,
468 conditions: vec![EventPropertyIs {
469 key: "type".to_owned(),
470 value: "org.matrix.msc3381.poll.start".into(),
471 }],
472 actions: vec![Notify],
473 }
474 }
475
476 /// Matches a poll end event sent in a room with exactly two members.
477 ///
478 /// This rule uses the unstable prefixes defined in [MSC3381] and [MSC3930].
479 ///
480 /// [MSC3381]: https://github.com/matrix-org/matrix-spec-proposals/pull/3381
481 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
482 #[cfg(feature = "unstable-msc3930")]
483 pub fn poll_end_one_to_one() -> Self {
484 Self {
485 rule_id: PredefinedUnderrideRuleId::PollEndOneToOne.to_string(),
486 default: true,
487 enabled: true,
488 conditions: vec![
489 RoomMemberCount {
490 is: RoomMemberCountIs::from(2),
491 },
492 EventPropertyIs {
493 key: "type".to_owned(),
494 value: "org.matrix.msc3381.poll.end".into(),
495 },
496 ],
497 actions: vec![Notify, SetTweak(Tweak::Sound("default".into()))],
498 }
499 }
500
501 /// Matches a poll end event sent in any room.
502 ///
503 /// This rule uses the unstable prefixes defined in [MSC3381] and [MSC3930].
504 ///
505 /// [MSC3381]: https://github.com/matrix-org/matrix-spec-proposals/pull/3381
506 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
507 #[cfg(feature = "unstable-msc3930")]
508 pub fn poll_end() -> Self {
509 Self {
510 rule_id: PredefinedUnderrideRuleId::PollEnd.to_string(),
511 default: true,
512 enabled: true,
513 conditions: vec![EventPropertyIs {
514 key: "type".to_owned(),
515 value: "org.matrix.msc3381.poll.end".into(),
516 }],
517 actions: vec![Notify],
518 }
519 }
520}
521
522/// The rule IDs of the predefined server push rules.
523#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
524#[non_exhaustive]
525pub enum PredefinedRuleId {
526 /// User-configured rules that override all other kinds.
527 Override(PredefinedOverrideRuleId),
528
529 /// Lowest priority user-defined rules.
530 Underride(PredefinedUnderrideRuleId),
531
532 /// Content-specific rules.
533 Content(PredefinedContentRuleId),
534}
535
536impl PredefinedRuleId {
537 /// Creates a string slice from this `PredefinedRuleId`.
538 pub fn as_str(&self) -> &str {
539 match self {
540 Self::Override(id) => id.as_str(),
541 Self::Underride(id) => id.as_str(),
542 Self::Content(id) => id.as_str(),
543 }
544 }
545
546 /// Get the kind of this `PredefinedRuleId`.
547 pub fn kind(&self) -> RuleKind {
548 match self {
549 Self::Override(id) => id.kind(),
550 Self::Underride(id) => id.kind(),
551 Self::Content(id) => id.kind(),
552 }
553 }
554}
555
556impl AsRef<str> for PredefinedRuleId {
557 fn as_ref(&self) -> &str {
558 self.as_str()
559 }
560}
561
562/// The rule IDs of the predefined override server push rules.
563#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
564#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
565#[palpo_enum(rename_all = ".m.rule.snake_case")]
566#[non_exhaustive]
567pub enum PredefinedOverrideRuleId {
568 /// `.m.rule.master`
569 Master,
570
571 /// `.m.rule.suppress_notices`
572 SuppressNotices,
573
574 /// `.m.rule.invite_for_me`
575 InviteForMe,
576
577 /// `.m.rule.member_event`
578 MemberEvent,
579
580 /// `.m.rule.is_user_mention`
581 IsUserMention,
582
583 /// `.m.rule.is_room_mention`
584 IsRoomMention,
585
586 /// `.m.rule.tombstone`
587 Tombstone,
588
589 /// `.m.rule.reaction`
590 Reaction,
591
592 /// `.m.rule.room.server_acl`
593 #[palpo_enum(rename = ".m.rule.room.server_acl")]
594 RoomServerAcl,
595
596 /// `.m.rule.suppress_edits`
597 SuppressEdits,
598
599 /// `.m.rule.poll_response`
600 ///
601 /// This uses the unstable prefix defined in [MSC3930].
602 ///
603 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
604 #[cfg(feature = "unstable-msc3930")]
605 #[palpo_enum(rename = ".org.matrix.msc3930.rule.poll_response")]
606 PollResponse,
607
608 #[doc(hidden)]
609 _Custom(PrivOwnedStr),
610}
611
612impl PredefinedOverrideRuleId {
613 /// Get the kind of this `PredefinedOverrideRuleId`.
614 pub fn kind(&self) -> RuleKind {
615 RuleKind::Override
616 }
617}
618
619/// The rule IDs of the predefined underride server push rules.
620#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
621#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
622#[palpo_enum(rename_all = ".m.rule.snake_case")]
623#[non_exhaustive]
624pub enum PredefinedUnderrideRuleId {
625 /// `.m.rule.call`
626 Call,
627
628 /// `.m.rule.encrypted_room_one_to_one`
629 EncryptedRoomOneToOne,
630
631 /// `.m.rule.room_one_to_one`
632 RoomOneToOne,
633
634 /// `.m.rule.message`
635 Message,
636
637 /// `.m.rule.encrypted`
638 Encrypted,
639
640 /// `.m.rule.poll_start_one_to_one`
641 ///
642 /// This uses the unstable prefix defined in [MSC3930].
643 ///
644 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
645 #[cfg(feature = "unstable-msc3930")]
646 #[palpo_enum(rename = ".org.matrix.msc3930.rule.poll_start_one_to_one")]
647 PollStartOneToOne,
648
649 /// `.m.rule.poll_start`
650 ///
651 /// This uses the unstable prefix defined in [MSC3930].
652 ///
653 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
654 #[cfg(feature = "unstable-msc3930")]
655 #[palpo_enum(rename = ".org.matrix.msc3930.rule.poll_start")]
656 PollStart,
657
658 /// `.m.rule.poll_end_one_to_one`
659 ///
660 /// This uses the unstable prefix defined in [MSC3930].
661 ///
662 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
663 #[cfg(feature = "unstable-msc3930")]
664 #[palpo_enum(rename = ".org.matrix.msc3930.rule.poll_end_one_to_one")]
665 PollEndOneToOne,
666
667 /// `.m.rule.poll_end`
668 ///
669 /// This uses the unstable prefix defined in [MSC3930].
670 ///
671 /// [MSC3930]: https://github.com/matrix-org/matrix-spec-proposals/pull/3930
672 #[cfg(feature = "unstable-msc3930")]
673 #[palpo_enum(rename = ".org.matrix.msc3930.rule.poll_end")]
674 PollEnd,
675
676 #[doc(hidden)]
677 _Custom(PrivOwnedStr),
678}
679
680impl PredefinedUnderrideRuleId {
681 /// Get the kind of this `PredefinedUnderrideRuleId`.
682 pub fn kind(&self) -> RuleKind {
683 RuleKind::Underride
684 }
685}
686
687/// The rule IDs of the predefined content server push rules.
688#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
689#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, StringEnum)]
690#[palpo_enum(rename_all = ".m.rule.snake_case")]
691#[non_exhaustive]
692pub enum PredefinedContentRuleId {
693 #[doc(hidden)]
694 _Custom(PrivOwnedStr),
695}
696
697impl PredefinedContentRuleId {
698 /// Get the kind of this `PredefinedContentRuleId`.
699 pub fn kind(&self) -> RuleKind {
700 RuleKind::Content
701 }
702}
703
704// #[cfg(test)]
705// mod tests {
706// use assert_matches2::assert_matches;
707// use assign::assign;
708
709// use super::PredefinedOverrideRuleId;
710// use crate::{
711// push::{Action, ConditionalPushRule, ConditionalPushRuleInit, Ruleset},
712// user_id,
713// };
714
715// #[test]
716// fn update_with_server_default() {
717// let user_rule_id = "user_always_true";
718// let default_rule_id = ".default_always_true";
719
720// let override_ = [
721// // Default `.m.rule.master` push rule with non-default state.
722// assign!(ConditionalPushRule::master(), { enabled: true, actions: vec![Action::Notify]}),
723// // User-defined push rule.
724// ConditionalPushRuleInit {
725// actions: vec![],
726// default: false,
727// enabled: false,
728// rule_id: user_rule_id.to_owned(),
729// conditions: vec![],
730// }
731// .into(),
732// // Old server-default push rule.
733// ConditionalPushRuleInit {
734// actions: vec![],
735// default: true,
736// enabled: true,
737// rule_id: default_rule_id.to_owned(),
738// conditions: vec![],
739// }
740// .into(),
741// ]
742// .into_iter()
743// .collect();
744// let mut ruleset = Ruleset {
745// override_,
746// ..Default::default()
747// };
748
749// let new_server_default = Ruleset::server_default(user_id!("@user:localhost"));
750
751// ruleset.update_with_server_default(new_server_default);
752
753// // Master rule is in first position.
754// let master_rule = &ruleset.override_[0];
755// assert_eq!(master_rule.rule_id, PredefinedOverrideRuleId::Master.as_str());
756
757// // `enabled` and `actions` have been copied from the old rules.
758// assert!(master_rule.enabled);
759// assert_eq!(master_rule.actions.len(), 1);
760// assert_matches!(&master_rule.actions[0], Action::Notify);
761
762// // Non-server-default rule is still present and hasn't changed.
763// let user_rule = ruleset.override_.get(user_rule_id).unwrap();
764// assert!(!user_rule.enabled);
765// assert_eq!(user_rule.actions.len(), 0);
766
767// // Old server-default rule is gone.
768// assert_matches!(ruleset.override_.get(default_rule_id), None);
769
770// // New server-default rule is present and hasn't changed.
771// let member_event_rule = ruleset
772// .override_
773// .get(PredefinedOverrideRuleId::MemberEvent.as_str())
774// .unwrap();
775// assert!(member_event_rule.enabled);
776// assert_eq!(member_event_rule.actions.len(), 0);
777// }
778// }