Skip to main content

palpo_core/push/
iter.rs

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