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#[derive(Clone, Debug)]
11#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
12pub enum AnyPushRule {
13 Override(ConditionalPushRule),
15
16 Content(PatternedPushRule),
18
19 Room(SimplePushRule<OwnedRoomId>),
21
22 Sender(SimplePushRule<OwnedUserId>),
24
25 Underride(ConditionalPushRule),
27}
28
29impl AnyPushRule {
30 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 pub fn enabled(&self) -> bool {
43 self.as_ref().enabled()
44 }
45
46 pub fn actions(&self) -> &[Action] {
48 self.as_ref().actions()
49 }
50
51 pub fn triggers_highlight(&self) -> bool {
53 self.as_ref().triggers_highlight()
54 }
55
56 pub fn triggers_notification(&self) -> bool {
58 self.as_ref().triggers_notification()
59 }
60
61 pub fn triggers_sound(&self) -> Option<&str> {
63 self.as_ref().triggers_sound()
64 }
65
66 pub fn rule_id(&self) -> &str {
68 self.as_ref().rule_id()
69 }
70
71 pub fn is_server_default(&self) -> bool {
73 self.as_ref().is_server_default()
74 }
75
76 pub fn applies(&self, event: &FlattenedJson, context: &PushConditionRoomCtx) -> bool {
83 self.as_ref().applies(event, context)
84 }
85}
86
87#[derive(Debug)]
89pub struct RulesetIntoIter {
90 content: IndexSetIntoIter<PatternedPushRule>,
91 override_: IndexSetIntoIter<ConditionalPushRule>,
92 room: IndexSetIntoIter<SimplePushRule<OwnedRoomId>>,
93 sender: IndexSetIntoIter<SimplePushRule<OwnedUserId>>,
94 underride: IndexSetIntoIter<ConditionalPushRule>,
95}
96
97impl Iterator for RulesetIntoIter {
98 type Item = AnyPushRule;
99
100 fn next(&mut self) -> Option<Self::Item> {
101 self.override_
102 .next()
103 .map(AnyPushRule::Override)
104 .or_else(|| self.content.next().map(AnyPushRule::Content))
105 .or_else(|| self.room.next().map(AnyPushRule::Room))
106 .or_else(|| self.sender.next().map(AnyPushRule::Sender))
107 .or_else(|| self.underride.next().map(AnyPushRule::Underride))
108 }
109}
110
111impl IntoIterator for Ruleset {
112 type Item = AnyPushRule;
113 type IntoIter = RulesetIntoIter;
114
115 fn into_iter(self) -> Self::IntoIter {
116 RulesetIntoIter {
117 content: self.content.into_iter(),
118 override_: self.override_.into_iter(),
119 room: self.room.into_iter(),
120 sender: self.sender.into_iter(),
121 underride: self.underride.into_iter(),
122 }
123 }
124}
125
126#[derive(Clone, Copy, Debug)]
128#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
129pub enum AnyPushRuleRef<'a> {
130 Override(&'a ConditionalPushRule),
132
133 Content(&'a PatternedPushRule),
135
136 Room(&'a SimplePushRule<OwnedRoomId>),
138
139 Sender(&'a SimplePushRule<OwnedUserId>),
141
142 Underride(&'a ConditionalPushRule),
144}
145
146impl<'a> AnyPushRuleRef<'a> {
147 pub fn to_owned(self) -> AnyPushRule {
149 match self {
150 Self::Override(o) => AnyPushRule::Override(o.clone()),
151 Self::Content(c) => AnyPushRule::Content(c.clone()),
152 Self::Room(r) => AnyPushRule::Room(r.clone()),
153 Self::Sender(s) => AnyPushRule::Sender(s.clone()),
154 Self::Underride(u) => AnyPushRule::Underride(u.clone()),
155 }
156 }
157
158 pub fn enabled(self) -> bool {
160 match self {
161 Self::Override(rule) => rule.enabled,
162 Self::Underride(rule) => rule.enabled,
163 Self::Content(rule) => rule.enabled,
164 Self::Room(rule) => rule.enabled,
165 Self::Sender(rule) => rule.enabled,
166 }
167 }
168
169 pub fn actions(self) -> &'a [Action] {
171 match self {
172 Self::Override(rule) => &rule.actions,
173 Self::Underride(rule) => &rule.actions,
174 Self::Content(rule) => &rule.actions,
175 Self::Room(rule) => &rule.actions,
176 Self::Sender(rule) => &rule.actions,
177 }
178 }
179
180 pub fn triggers_highlight(self) -> bool {
182 self.actions().iter().any(|a| a.is_highlight())
183 }
184
185 pub fn triggers_notification(self) -> bool {
187 self.actions().iter().any(|a| a.should_notify())
188 }
189
190 pub fn triggers_sound(self) -> Option<&'a str> {
192 self.actions().iter().find_map(|a| a.sound())
193 }
194
195 pub fn rule_id(self) -> &'a str {
197 match self {
198 Self::Override(rule) => &rule.rule_id,
199 Self::Underride(rule) => &rule.rule_id,
200 Self::Content(rule) => &rule.rule_id,
201 Self::Room(rule) => rule.rule_id.as_ref(),
202 Self::Sender(rule) => rule.rule_id.as_ref(),
203 }
204 }
205
206 pub fn is_server_default(self) -> bool {
208 match self {
209 Self::Override(rule) => rule.default,
210 Self::Underride(rule) => rule.default,
211 Self::Content(rule) => rule.default,
212 Self::Room(rule) => rule.default,
213 Self::Sender(rule) => rule.default,
214 }
215 }
216
217 pub fn applies(self, event: &FlattenedJson, context: &PushConditionRoomCtx) -> bool {
224 if event.get_str("sender").is_some_and(|sender| sender == context.user_id) {
225 return false;
226 }
227
228 match self {
229 Self::Override(rule) => rule.applies(event, context),
230 Self::Underride(rule) => rule.applies(event, context),
231 Self::Content(rule) => rule.applies_to("content.body", event, context),
232 Self::Room(rule) => {
233 rule.enabled
234 && condition::check_event_match(
235 event,
236 "room_id",
237 rule.rule_id.as_ref(),
238 context,
239 )
240 }
241 Self::Sender(rule) => {
242 rule.enabled
243 && condition::check_event_match(event, "sender", rule.rule_id.as_ref(), context)
244 }
245 }
246 }
247}
248
249#[derive(Debug)]
251pub struct RulesetIter<'a> {
252 content: IndexSetIter<'a, PatternedPushRule>,
253 override_: IndexSetIter<'a, ConditionalPushRule>,
254 room: IndexSetIter<'a, SimplePushRule<OwnedRoomId>>,
255 sender: IndexSetIter<'a, SimplePushRule<OwnedUserId>>,
256 underride: IndexSetIter<'a, ConditionalPushRule>,
257}
258
259impl<'a> Iterator for RulesetIter<'a> {
260 type Item = AnyPushRuleRef<'a>;
261
262 fn next(&mut self) -> Option<Self::Item> {
263 self.override_
264 .next()
265 .map(AnyPushRuleRef::Override)
266 .or_else(|| self.content.next().map(AnyPushRuleRef::Content))
267 .or_else(|| self.room.next().map(AnyPushRuleRef::Room))
268 .or_else(|| self.sender.next().map(AnyPushRuleRef::Sender))
269 .or_else(|| self.underride.next().map(AnyPushRuleRef::Underride))
270 }
271}
272
273impl<'a> IntoIterator for &'a Ruleset {
274 type Item = AnyPushRuleRef<'a>;
275 type IntoIter = RulesetIter<'a>;
276
277 fn into_iter(self) -> Self::IntoIter {
278 RulesetIter {
279 content: self.content.iter(),
280 override_: self.override_.iter(),
281 room: self.room.iter(),
282 sender: self.sender.iter(),
283 underride: self.underride.iter(),
284 }
285 }
286}