1pub mod create_filter;
4pub mod get_filter;
5
6mod lazy_load;
7mod url;
8
9use js_int::UInt;
10use ruma_common::{OwnedRoomId, OwnedUserId, serde::StringEnum};
11use serde::{Deserialize, Serialize};
12
13pub use self::{lazy_load::LazyLoadOptions, url::UrlFilter};
14use crate::PrivOwnedStr;
15
16#[doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/doc/string_enum.md"))]
18#[derive(Clone, Default, StringEnum)]
19#[ruma_enum(rename_all = "snake_case")]
20#[non_exhaustive]
21pub enum EventFormat {
22 #[default]
24 Client,
25
26 Federation,
28
29 #[doc(hidden)]
30 _Custom(PrivOwnedStr),
31}
32
33#[derive(Clone, Debug, Default, Deserialize, Serialize)]
35#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
36pub struct RoomEventFilter {
37 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
43 pub not_types: Vec<String>,
44
45 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
50 pub not_rooms: Vec<OwnedRoomId>,
51
52 #[serde(skip_serializing_if = "Option::is_none")]
54 pub limit: Option<UInt>,
55
56 #[serde(skip_serializing_if = "Option::is_none")]
60 pub rooms: Option<Vec<OwnedRoomId>>,
61
62 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
67 pub not_senders: Vec<OwnedUserId>,
68
69 #[serde(skip_serializing_if = "Option::is_none")]
73 pub senders: Option<Vec<OwnedUserId>>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
80 pub types: Option<Vec<String>>,
81
82 #[serde(rename = "contains_url", skip_serializing_if = "Option::is_none")]
88 pub url_filter: Option<UrlFilter>,
89
90 #[serde(flatten)]
94 pub lazy_load_options: LazyLoadOptions,
95
96 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
103 pub unread_thread_notifications: bool,
104}
105
106impl RoomEventFilter {
107 pub fn empty() -> Self {
111 Self::default()
112 }
113
114 pub fn ignore_all() -> Self {
116 Self { types: Some(vec![]), ..Default::default() }
117 }
118
119 pub fn with_lazy_loading() -> Self {
125 Self {
126 lazy_load_options: LazyLoadOptions::Enabled { include_redundant_members: false },
127 ..Default::default()
128 }
129 }
130
131 pub fn is_empty(&self) -> bool {
133 let Self {
134 not_types,
135 not_rooms,
136 limit,
137 rooms,
138 not_senders,
139 senders,
140 types,
141 url_filter,
142 lazy_load_options,
143 unread_thread_notifications,
144 } = self;
145 not_types.is_empty()
146 && not_rooms.is_empty()
147 && limit.is_none()
148 && rooms.is_none()
149 && not_senders.is_empty()
150 && senders.is_none()
151 && types.is_none()
152 && url_filter.is_none()
153 && lazy_load_options.is_disabled()
154 && !unread_thread_notifications
155 }
156}
157
158#[derive(Clone, Debug, Default, Deserialize, Serialize)]
160#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
161pub struct RoomFilter {
162 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
166 pub include_leave: bool,
167
168 #[serde(default, skip_serializing_if = "ruma_common::serde::is_empty")]
170 pub account_data: RoomEventFilter,
171
172 #[serde(default, skip_serializing_if = "ruma_common::serde::is_empty")]
174 pub timeline: RoomEventFilter,
175
176 #[serde(default, skip_serializing_if = "ruma_common::serde::is_empty")]
179 pub ephemeral: RoomEventFilter,
180
181 #[serde(default, skip_serializing_if = "ruma_common::serde::is_empty")]
183 pub state: RoomEventFilter,
184
185 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
191 pub not_rooms: Vec<OwnedRoomId>,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
198 pub rooms: Option<Vec<OwnedRoomId>>,
199}
200
201impl RoomFilter {
202 pub fn empty() -> Self {
206 Self::default()
207 }
208
209 pub fn ignore_all() -> Self {
211 Self { rooms: Some(vec![]), ..Default::default() }
212 }
213
214 pub fn with_lazy_loading() -> Self {
220 Self { state: RoomEventFilter::with_lazy_loading(), ..Default::default() }
221 }
222
223 pub fn is_empty(&self) -> bool {
225 let Self { include_leave, account_data, timeline, ephemeral, state, not_rooms, rooms } =
226 self;
227 !include_leave
228 && account_data.is_empty()
229 && timeline.is_empty()
230 && ephemeral.is_empty()
231 && state.is_empty()
232 && not_rooms.is_empty()
233 && rooms.is_none()
234 }
235}
236
237#[derive(Clone, Debug, Default, Deserialize, Serialize)]
239#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
240pub struct Filter {
241 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
247 pub not_types: Vec<String>,
248
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub limit: Option<UInt>,
252
253 #[serde(skip_serializing_if = "Option::is_none")]
257 pub senders: Option<Vec<OwnedUserId>>,
258
259 #[serde(skip_serializing_if = "Option::is_none")]
264 pub types: Option<Vec<String>>,
265
266 #[serde(default, skip_serializing_if = "<[_]>::is_empty")]
271 pub not_senders: Vec<OwnedUserId>,
272}
273
274impl Filter {
275 pub fn empty() -> Self {
279 Self::default()
280 }
281
282 pub fn ignore_all() -> Self {
284 Self { types: Some(vec![]), ..Default::default() }
285 }
286
287 pub fn is_empty(&self) -> bool {
289 let Self { not_types, limit, senders, types, not_senders } = self;
290 not_types.is_empty()
291 && limit.is_none()
292 && senders.is_none()
293 && types.is_none()
294 && not_senders.is_empty()
295 }
296}
297
298#[derive(Clone, Debug, Default, Deserialize, Serialize)]
300#[cfg_attr(not(ruma_unstable_exhaustive_types), non_exhaustive)]
301pub struct FilterDefinition {
302 #[serde(skip_serializing_if = "Option::is_none")]
309 pub event_fields: Option<Vec<String>>,
310
311 #[serde(default, skip_serializing_if = "ruma_common::serde::is_default")]
316 pub event_format: EventFormat,
317
318 #[serde(default, skip_serializing_if = "ruma_common::serde::is_empty")]
320 pub presence: Filter,
321
322 #[serde(default, skip_serializing_if = "ruma_common::serde::is_empty")]
324 pub account_data: Filter,
325
326 #[serde(default, skip_serializing_if = "ruma_common::serde::is_empty")]
328 pub room: RoomFilter,
329}
330
331impl FilterDefinition {
332 pub fn empty() -> Self {
336 Self::default()
337 }
338
339 pub fn ignore_all() -> Self {
341 Self {
342 account_data: Filter::ignore_all(),
343 room: RoomFilter::ignore_all(),
344 presence: Filter::ignore_all(),
345 ..Default::default()
346 }
347 }
348
349 pub fn with_lazy_loading() -> Self {
355 Self { room: RoomFilter::with_lazy_loading(), ..Default::default() }
356 }
357
358 pub fn is_empty(&self) -> bool {
360 let Self { event_fields, event_format, presence, account_data, room } = self;
361 event_fields.is_none()
362 && *event_format == EventFormat::Client
363 && presence.is_empty()
364 && account_data.is_empty()
365 && room.is_empty()
366 }
367}
368
369macro_rules! can_be_empty {
370 ($ty:ident) => {
371 impl ruma_common::serde::CanBeEmpty for $ty {
372 fn is_empty(&self) -> bool {
373 self.is_empty()
374 }
375 }
376 };
377}
378
379can_be_empty!(Filter);
380can_be_empty!(FilterDefinition);
381can_be_empty!(RoomEventFilter);
382can_be_empty!(RoomFilter);
383
384#[cfg(test)]
385mod tests {
386 use ruma_common::canonical_json::assert_to_canonical_json_eq;
387 use serde_json::{
388 from_str as from_json_str, from_value as from_json_value, json, to_string as to_json_string,
389 };
390
391 use super::{
392 Filter, FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter, UrlFilter,
393 };
394
395 #[test]
396 fn default_filters_are_empty() {
397 assert_to_canonical_json_eq!(Filter::default(), json!({}));
398 assert_to_canonical_json_eq!(FilterDefinition::default(), json!({}));
399 assert_to_canonical_json_eq!(RoomEventFilter::default(), json!({}));
400 assert_to_canonical_json_eq!(RoomFilter::default(), json!({}));
401 }
402
403 #[test]
404 fn filter_definition_roundtrip() {
405 let filter = FilterDefinition::default();
406 assert_to_canonical_json_eq!(filter, json!({}));
407
408 let filter_str = to_json_string(&filter).unwrap();
409 let incoming_filter = from_json_str::<FilterDefinition>(&filter_str).unwrap();
410 assert!(incoming_filter.is_empty());
411 }
412
413 #[test]
414 fn room_filter_definition_roundtrip() {
415 let filter = RoomFilter::default();
416 assert_to_canonical_json_eq!(filter, json!({}));
417
418 let filter_str = to_json_string(&filter).unwrap();
419 let incoming_room_filter = from_json_str::<RoomFilter>(&filter_str).unwrap();
420 assert!(incoming_room_filter.is_empty());
421 }
422
423 #[test]
424 fn issue_366() {
425 let obj = json!({
426 "lazy_load_members": true,
427 "filter_json": { "contains_url": true, "types": ["m.room.message"] },
428 "types": ["m.room.message"],
429 "not_types": [],
430 "rooms": null,
431 "not_rooms": [],
432 "senders": null,
433 "not_senders": [],
434 "contains_url": true,
435 });
436
437 let filter: RoomEventFilter = from_json_value(obj).unwrap();
438
439 assert_eq!(filter.types, Some(vec!["m.room.message".to_owned()]));
440 assert_eq!(filter.not_types, vec![""; 0]);
441 assert_eq!(filter.rooms, None);
442 assert_eq!(filter.not_rooms, vec![""; 0]);
443 assert_eq!(filter.senders, None);
444 assert_eq!(filter.not_senders, vec![""; 0]);
445 assert_eq!(filter.limit, None);
446 assert_eq!(filter.url_filter, Some(UrlFilter::EventsWithUrl));
447 assert_eq!(
448 filter.lazy_load_options,
449 LazyLoadOptions::Enabled { include_redundant_members: false }
450 );
451 }
452}