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 {
59 self.as_ref().triggers_notification()
60 }
61
62 #[cfg(feature = "unstable-msc3768")]
64 pub fn triggers_remote_notification(&self) -> bool {
65 self.as_ref().triggers_remote_notification()
66 }
67
68 pub fn triggers_sound(&self) -> Option<&str> {
70 self.as_ref().triggers_sound()
71 }
72
73 pub fn rule_id(&self) -> &str {
75 self.as_ref().rule_id()
76 }
77
78 pub fn is_server_default(&self) -> bool {
80 self.as_ref().is_server_default()
81 }
82
83 pub async fn applies(&self, event: &FlattenedJson, context: &PushConditionRoomCtx) -> bool {
90 self.as_ref().applies(event, context).await
91 }
92}
93
94#[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#[derive(Clone, Copy, Debug)]
135#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
136pub enum AnyPushRuleRef<'a> {
137 Override(&'a ConditionalPushRule),
139
140 Content(&'a PatternedPushRule),
142
143 Room(&'a SimplePushRule<OwnedRoomId>),
145
146 Sender(&'a SimplePushRule<OwnedUserId>),
148
149 Underride(&'a ConditionalPushRule),
151}
152
153impl<'a> AnyPushRuleRef<'a> {
154 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 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 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 pub fn triggers_highlight(self) -> bool {
189 self.actions().iter().any(|a| a.is_highlight())
190 }
191
192 pub fn triggers_notification(self) -> bool {
195 self.actions().iter().any(|a| a.should_notify())
196 }
197
198 #[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 pub fn triggers_sound(self) -> Option<&'a str> {
206 self.actions().iter().find_map(|a| a.sound())
207 }
208
209 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 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 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#[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}