ruma_common/push/
iter.rs

1use indexmap::set::{IntoIter as IndexSetIntoIter, Iter as IndexSetIter};
2
3use super::{
4    condition, Action, ConditionalPushRule, FlattenedJson, PatternedPushRule, PushConditionRoomCtx,
5    Ruleset, SimplePushRule,
6};
7use crate::{OwnedRoomId, OwnedUserId};
8
9/// The kinds of push rules that are available.
10#[derive(Clone, Debug)]
11#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
12pub enum AnyPushRule {
13    /// Rules that override all other kinds.
14    Override(ConditionalPushRule),
15
16    /// Content-specific rules.
17    Content(PatternedPushRule),
18
19    /// Room-specific rules.
20    Room(SimplePushRule<OwnedRoomId>),
21
22    /// Sender-specific rules.
23    Sender(SimplePushRule<OwnedUserId>),
24
25    /// Lowest priority rules.
26    Underride(ConditionalPushRule),
27}
28
29impl AnyPushRule {
30    /// Convert `AnyPushRule` to `AnyPushRuleRef`.
31    pub fn as_ref(&self) -> AnyPushRuleRef<'_> {
32        match self {
33            Self::Override(o) => AnyPushRuleRef::Override(o),
34            Self::Content(c) => AnyPushRuleRef::Content(c),
35            Self::Room(r) => AnyPushRuleRef::Room(r),
36            Self::Sender(s) => AnyPushRuleRef::Sender(s),
37            Self::Underride(u) => AnyPushRuleRef::Underride(u),
38        }
39    }
40
41    /// Get the `enabled` flag of the push rule.
42    pub fn enabled(&self) -> bool {
43        self.as_ref().enabled()
44    }
45
46    /// Get the `actions` of the push rule.
47    pub fn actions(&self) -> &[Action] {
48        self.as_ref().actions()
49    }
50
51    /// Whether an event that matches the push rule should be highlighted.
52    pub fn triggers_highlight(&self) -> bool {
53        self.as_ref().triggers_highlight()
54    }
55
56    /// Whether an event that matches the push rule should trigger a notification (either in-app or
57    /// remote / push).
58    pub fn triggers_notification(&self) -> bool {
59        self.as_ref().triggers_notification()
60    }
61
62    /// Whether an event that matches the push rule should trigger a remote notification.
63    #[cfg(feature = "unstable-msc3768")]
64    pub fn triggers_remote_notification(&self) -> bool {
65        self.as_ref().triggers_remote_notification()
66    }
67
68    /// The sound that should be played when an event matches the push rule, if any.
69    pub fn triggers_sound(&self) -> Option<&str> {
70        self.as_ref().triggers_sound()
71    }
72
73    /// Get the `rule_id` of the push rule.
74    pub fn rule_id(&self) -> &str {
75        self.as_ref().rule_id()
76    }
77
78    /// Whether the push rule is a server-default rule.
79    pub fn is_server_default(&self) -> bool {
80        self.as_ref().is_server_default()
81    }
82
83    /// Check if the push rule applies to the event.
84    ///
85    /// # Arguments
86    ///
87    /// * `event` - The flattened JSON representation of a room message event.
88    /// * `context` - The context of the room at the time of the event.
89    pub async fn applies(&self, event: &FlattenedJson, context: &PushConditionRoomCtx) -> bool {
90        self.as_ref().applies(event, context).await
91    }
92}
93
94/// Iterator type for `Ruleset`
95#[derive(Debug)]
96pub struct RulesetIntoIter {
97    content: IndexSetIntoIter<PatternedPushRule>,
98    override_: IndexSetIntoIter<ConditionalPushRule>,
99    room: IndexSetIntoIter<SimplePushRule<OwnedRoomId>>,
100    sender: IndexSetIntoIter<SimplePushRule<OwnedUserId>>,
101    underride: IndexSetIntoIter<ConditionalPushRule>,
102}
103
104impl Iterator for RulesetIntoIter {
105    type Item = AnyPushRule;
106
107    fn next(&mut self) -> Option<Self::Item> {
108        self.override_
109            .next()
110            .map(AnyPushRule::Override)
111            .or_else(|| self.content.next().map(AnyPushRule::Content))
112            .or_else(|| self.room.next().map(AnyPushRule::Room))
113            .or_else(|| self.sender.next().map(AnyPushRule::Sender))
114            .or_else(|| self.underride.next().map(AnyPushRule::Underride))
115    }
116}
117
118impl IntoIterator for Ruleset {
119    type Item = AnyPushRule;
120    type IntoIter = RulesetIntoIter;
121
122    fn into_iter(self) -> Self::IntoIter {
123        RulesetIntoIter {
124            content: self.content.into_iter(),
125            override_: self.override_.into_iter(),
126            room: self.room.into_iter(),
127            sender: self.sender.into_iter(),
128            underride: self.underride.into_iter(),
129        }
130    }
131}
132
133/// Reference to any kind of push rule.
134#[derive(Clone, Copy, Debug)]
135#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
136pub enum AnyPushRuleRef<'a> {
137    /// Rules that override all other kinds.
138    Override(&'a ConditionalPushRule),
139
140    /// Content-specific rules.
141    Content(&'a PatternedPushRule),
142
143    /// Room-specific rules.
144    Room(&'a SimplePushRule<OwnedRoomId>),
145
146    /// Sender-specific rules.
147    Sender(&'a SimplePushRule<OwnedUserId>),
148
149    /// Lowest priority rules.
150    Underride(&'a ConditionalPushRule),
151}
152
153impl<'a> AnyPushRuleRef<'a> {
154    /// Convert `AnyPushRuleRef` to `AnyPushRule` by cloning the inner value.
155    pub fn to_owned(self) -> AnyPushRule {
156        match self {
157            Self::Override(o) => AnyPushRule::Override(o.clone()),
158            Self::Content(c) => AnyPushRule::Content(c.clone()),
159            Self::Room(r) => AnyPushRule::Room(r.clone()),
160            Self::Sender(s) => AnyPushRule::Sender(s.clone()),
161            Self::Underride(u) => AnyPushRule::Underride(u.clone()),
162        }
163    }
164
165    /// Get the `enabled` flag of the push rule.
166    pub fn enabled(self) -> bool {
167        match self {
168            Self::Override(rule) => rule.enabled,
169            Self::Underride(rule) => rule.enabled,
170            Self::Content(rule) => rule.enabled,
171            Self::Room(rule) => rule.enabled,
172            Self::Sender(rule) => rule.enabled,
173        }
174    }
175
176    /// Get the `actions` of the push rule.
177    pub fn actions(self) -> &'a [Action] {
178        match self {
179            Self::Override(rule) => &rule.actions,
180            Self::Underride(rule) => &rule.actions,
181            Self::Content(rule) => &rule.actions,
182            Self::Room(rule) => &rule.actions,
183            Self::Sender(rule) => &rule.actions,
184        }
185    }
186
187    /// Whether an event that matches the push rule should be highlighted.
188    pub fn triggers_highlight(self) -> bool {
189        self.actions().iter().any(|a| a.is_highlight())
190    }
191
192    /// Whether an event that matches the push rule should trigger a notification (either in-app or
193    /// remote / push).
194    pub fn triggers_notification(self) -> bool {
195        self.actions().iter().any(|a| a.should_notify())
196    }
197
198    /// Whether an event that matches the push rule should trigger a remote notification.
199    #[cfg(feature = "unstable-msc3768")]
200    pub fn triggers_remote_notification(self) -> bool {
201        self.actions().iter().any(|a| a.should_notify_remote())
202    }
203
204    /// The sound that should be played when an event matches the push rule, if any.
205    pub fn triggers_sound(self) -> Option<&'a str> {
206        self.actions().iter().find_map(|a| a.sound())
207    }
208
209    /// Get the `rule_id` of the push rule.
210    pub fn rule_id(self) -> &'a str {
211        match self {
212            Self::Override(rule) => &rule.rule_id,
213            Self::Underride(rule) => &rule.rule_id,
214            Self::Content(rule) => &rule.rule_id,
215            Self::Room(rule) => rule.rule_id.as_ref(),
216            Self::Sender(rule) => rule.rule_id.as_ref(),
217        }
218    }
219
220    /// Whether the push rule is a server-default rule.
221    pub fn is_server_default(self) -> bool {
222        match self {
223            Self::Override(rule) => rule.default,
224            Self::Underride(rule) => rule.default,
225            Self::Content(rule) => rule.default,
226            Self::Room(rule) => rule.default,
227            Self::Sender(rule) => rule.default,
228        }
229    }
230
231    /// Check if the push rule applies to the event.
232    ///
233    /// # Arguments
234    ///
235    /// * `event` - The flattened JSON representation of a room message event.
236    /// * `context` - The context of the room at the time of the event.
237    pub async fn applies(self, event: &FlattenedJson, context: &PushConditionRoomCtx) -> bool {
238        if event.get_str("sender").is_some_and(|sender| sender == context.user_id) {
239            return false;
240        }
241
242        match self {
243            Self::Override(rule) => rule.applies(event, context).await,
244            Self::Underride(rule) => rule.applies(event, context).await,
245            Self::Content(rule) => rule.applies_to("content.body", event, context),
246            Self::Room(rule) => {
247                rule.enabled
248                    && condition::check_event_match(
249                        event,
250                        "room_id",
251                        rule.rule_id.as_ref(),
252                        context,
253                    )
254            }
255            Self::Sender(rule) => {
256                rule.enabled
257                    && condition::check_event_match(event, "sender", rule.rule_id.as_ref(), context)
258            }
259        }
260    }
261}
262
263/// Iterator type for `Ruleset`
264#[derive(Debug)]
265pub struct RulesetIter<'a> {
266    content: IndexSetIter<'a, PatternedPushRule>,
267    override_: IndexSetIter<'a, ConditionalPushRule>,
268    room: IndexSetIter<'a, SimplePushRule<OwnedRoomId>>,
269    sender: IndexSetIter<'a, SimplePushRule<OwnedUserId>>,
270    underride: IndexSetIter<'a, ConditionalPushRule>,
271}
272
273impl<'a> Iterator for RulesetIter<'a> {
274    type Item = AnyPushRuleRef<'a>;
275
276    fn next(&mut self) -> Option<Self::Item> {
277        self.override_
278            .next()
279            .map(AnyPushRuleRef::Override)
280            .or_else(|| self.content.next().map(AnyPushRuleRef::Content))
281            .or_else(|| self.room.next().map(AnyPushRuleRef::Room))
282            .or_else(|| self.sender.next().map(AnyPushRuleRef::Sender))
283            .or_else(|| self.underride.next().map(AnyPushRuleRef::Underride))
284    }
285}
286
287impl<'a> IntoIterator for &'a Ruleset {
288    type Item = AnyPushRuleRef<'a>;
289    type IntoIter = RulesetIter<'a>;
290
291    fn into_iter(self) -> Self::IntoIter {
292        RulesetIter {
293            content: self.content.iter(),
294            override_: self.override_.iter(),
295            room: self.room.iter(),
296            sender: self.sender.iter(),
297            underride: self.underride.iter(),
298        }
299    }
300}