diff --git a/ruma-client-api/src/r0/filter.rs b/ruma-client-api/src/r0/filter.rs index 50045978..0cd59beb 100644 --- a/ruma-client-api/src/r0/filter.rs +++ b/ruma-client-api/src/r0/filter.rs @@ -14,8 +14,9 @@ use ruma_common::Outgoing; use ruma_identifiers::{RoomId, UserId}; use serde::{Deserialize, Serialize}; -/// Format to use for returned events -#[derive(Copy, Clone, Debug, Deserialize, Serialize)] +/// Format to use for returned events. +#[derive(Copy, Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[serde(rename_all = "snake_case")] pub enum EventFormat { /// Client format, as described in the Client API. @@ -25,8 +26,15 @@ pub enum EventFormat { Federation, } -/// Filters to be applied to room events +impl Default for EventFormat { + fn default() -> Self { + Self::Client + } +} + +/// Filters to be applied to room events. #[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[incoming_derive(Clone, Serialize)] pub struct RoomEventFilter<'a> { /// A list of event types to exclude. @@ -88,14 +96,50 @@ pub struct RoomEventFilter<'a> { } impl<'a> RoomEventFilter<'a> { - /// A filter that can be used to ignore all room events + /// Creates an empty `RoomEventFilter`. + /// + /// You can also use the [`Default`] implementation. + pub fn empty() -> Self { + Self::default() + } + + /// Creates a `RoomEventFilter` that can be used to ignore all room events. pub fn ignore_all() -> Self { Self { types: Some(&[]), ..Default::default() } } + + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + self.not_types.is_empty() + && self.not_rooms.is_empty() + && self.limit.is_none() + && self.rooms.is_none() + && self.not_senders.is_empty() + && self.senders.is_none() + && self.types.is_none() + && self.url_filter.is_none() + && self.lazy_load_options.is_disabled() + } } -/// Filters to be applied to room data +impl IncomingRoomEventFilter { + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + self.not_types.is_empty() + && self.not_rooms.is_empty() + && self.limit.is_none() + && self.rooms.is_none() + && self.not_senders.is_empty() + && self.senders.is_none() + && self.types.is_none() + && self.url_filter.is_none() + && self.lazy_load_options.is_disabled() + } +} + +/// Filters to be applied to room data. #[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[incoming_derive(Clone, Serialize)] pub struct RoomFilter<'a> { /// Include rooms that the user has left in the sync. @@ -105,21 +149,21 @@ pub struct RoomFilter<'a> { pub include_leave: bool, /// The per user account data to include for rooms. - #[serde(skip_serializing_if = "Option::is_none")] - pub account_data: Option>, + #[serde(skip_serializing_if = "ruma_serde::is_empty")] + pub account_data: RoomEventFilter<'a>, /// The message and state update events to include for rooms. - #[serde(skip_serializing_if = "Option::is_none")] - pub timeline: Option>, + #[serde(skip_serializing_if = "ruma_serde::is_empty")] + pub timeline: RoomEventFilter<'a>, /// The events that aren't recorded in the room history, e.g. typing and receipts, to include /// for rooms. - #[serde(skip_serializing_if = "Option::is_none")] - pub ephemeral: Option>, + #[serde(skip_serializing_if = "ruma_serde::is_empty")] + pub ephemeral: RoomEventFilter<'a>, /// The state events to include for rooms. - #[serde(skip_serializing_if = "Option::is_none")] - pub state: Option>, + #[serde(skip_serializing_if = "ruma_serde::is_empty")] + pub state: RoomEventFilter<'a>, /// A list of room IDs to exclude. /// @@ -138,14 +182,46 @@ pub struct RoomFilter<'a> { } impl<'a> RoomFilter<'a> { - /// A filter that can be used to ignore all room events (of any type) + /// Creates an empty `RoomFilter`. + /// + /// You can also use the [`Default`] implementation. + pub fn empty() -> Self { + Self::default() + } + + /// Creates a `RoomFilter` that can be used to ignore all room events (of any type). pub fn ignore_all() -> Self { Self { rooms: Some(&[]), ..Default::default() } } + + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + !self.include_leave + && self.account_data.is_empty() + && self.timeline.is_empty() + && self.ephemeral.is_empty() + && self.state.is_empty() + && self.not_rooms.is_empty() + && self.rooms.is_none() + } } -/// Filter for not-room data +impl IncomingRoomFilter { + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + !self.include_leave + && self.account_data.is_empty() + && self.timeline.is_empty() + && self.ephemeral.is_empty() + && self.state.is_empty() + && self.not_rooms.is_empty() + && self.rooms.is_none() + } +} + +/// Filter for non-room data. #[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[incoming_derive(Clone, Serialize)] pub struct Filter<'a> { /// A list of event types to exclude. @@ -182,14 +258,42 @@ pub struct Filter<'a> { } impl<'a> Filter<'a> { - /// A filter that can be used to ignore all events + /// Creates an empty `Filter`. + /// + /// You can also use the [`Default`] implementation. + pub fn empty() -> Self { + Self::default() + } + + /// Creates a `Filter` that can be used to ignore all events. pub fn ignore_all() -> Self { Self { types: Some(&[]), ..Default::default() } } + + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + self.not_types.is_empty() + && self.limit.is_none() + && self.senders.is_none() + && self.types.is_none() + && self.not_senders.is_empty() + } +} + +impl IncomingFilter { + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + self.not_types.is_empty() + && self.limit.is_none() + && self.senders.is_none() + && self.types.is_none() + && self.not_senders.is_empty() + } } /// A filter definition #[derive(Clone, Copy, Debug, Default, Outgoing, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[incoming_derive(Clone, Serialize)] pub struct FilterDefinition<'a> { /// List of event fields to include. @@ -198,41 +302,88 @@ pub struct FilterDefinition<'a> { /// to indicate sub-fields. So ['content.body'] will include the 'body' field of the 'content' /// object. A literal '.' character in a field name may be escaped using a '\'. A server may /// include more fields than were requested. - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(skip_serializing_if = "Option::is_none")] pub event_fields: Option<&'a [String]>, /// The format to use for events. /// /// 'client' will return the events in a format suitable for clients. 'federation' will return /// the raw event as received over federation. The default is 'client'. - #[serde(skip_serializing_if = "Option::is_none")] - pub event_format: Option, + #[serde(default, skip_serializing_if = "ruma_serde::is_default")] + pub event_format: EventFormat, /// The presence updates to include. - #[serde(skip_serializing_if = "Option::is_none")] - pub presence: Option>, + #[serde(skip_serializing_if = "ruma_serde::is_empty")] + pub presence: Filter<'a>, /// The user account data that isn't associated with rooms to include. - #[serde(skip_serializing_if = "Option::is_none")] - pub account_data: Option>, + #[serde(skip_serializing_if = "ruma_serde::is_empty")] + pub account_data: Filter<'a>, /// Filters to be applied to room data. - #[serde(skip_serializing_if = "Option::is_none")] - pub room: Option>, + #[serde(skip_serializing_if = "ruma_serde::is_empty")] + pub room: RoomFilter<'a>, } impl<'a> FilterDefinition<'a> { - /// A filter that can be used to ignore all events + /// Creates an empty `FilterDefinition`. + /// + /// You can also use the [`Default`] implementation. + pub fn empty() -> Self { + Self::default() + } + + /// Creates a `FilterDefinition` that can be used to ignore all events. pub fn ignore_all() -> Self { Self { - account_data: Some(Filter::ignore_all()), - room: Some(RoomFilter::ignore_all()), - presence: Some(Filter::ignore_all()), + account_data: Filter::ignore_all(), + room: RoomFilter::ignore_all(), + presence: Filter::ignore_all(), ..Default::default() } } + + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + self.event_fields.is_none() + && self.event_format == EventFormat::Client + && self.presence.is_empty() + && self.account_data.is_empty() + && self.room.is_empty() + } } +impl IncomingFilterDefinition { + /// Returns `true` if all fields are empty. + pub fn is_empty(&self) -> bool { + self.event_fields.is_none() + && self.event_format == EventFormat::Client + && self.presence.is_empty() + && self.account_data.is_empty() + && self.room.is_empty() + } +} + +macro_rules! can_be_empty { + ($ty:ident $(<$gen:tt>)?) => { + impl $(<$gen>)? ruma_serde::CanBeEmpty for $ty $(<$gen>)? { + fn is_empty(&self) -> bool { + self.is_empty() + } + } + }; +} + +can_be_empty!(Filter<'a>); +can_be_empty!(FilterDefinition<'a>); +can_be_empty!(RoomEventFilter<'a>); +can_be_empty!(RoomFilter<'a>); + +can_be_empty!(IncomingFilter); +can_be_empty!(IncomingFilterDefinition); +can_be_empty!(IncomingRoomEventFilter); +can_be_empty!(IncomingRoomFilter); + #[cfg(test)] mod tests { use serde_json::{json, to_value as to_json_value}; diff --git a/ruma-client-api/src/r0/filter/lazy_load.rs b/ruma-client-api/src/r0/filter/lazy_load.rs index a8077981..37b35a6b 100644 --- a/ruma-client-api/src/r0/filter/lazy_load.rs +++ b/ruma-client-api/src/r0/filter/lazy_load.rs @@ -22,6 +22,13 @@ pub enum LazyLoadOptions { }, } +impl LazyLoadOptions { + /// Returns `true` is `self` is `Disabled`. + pub fn is_disabled(&self) -> bool { + matches!(self, Self::Disabled) + } +} + impl Serialize for LazyLoadOptions { fn serialize(&self, serializer: S) -> Result where