diff --git a/src/r0/message/get_message_events.rs b/src/r0/message/get_message_events.rs index cd8fa749..3d7923ff 100644 --- a/src/r0/message/get_message_events.rs +++ b/src/r0/message/get_message_events.rs @@ -47,8 +47,12 @@ ruma_api! { #[ruma_api(query)] pub limit: Option, /// A RoomEventFilter to filter returned events with. - #[serde(skip_serializing_if = "Option::is_none")] #[ruma_api(query)] + #[serde( + with = "crate::serde::json_string", + default, + skip_serializing_if = "Option::is_none" + )] pub filter: Option, } @@ -75,3 +79,80 @@ pub enum Direction { #[serde(rename = "f")] Forward, } + +#[cfg(test)] +mod tests { + use super::{Direction, Request}; + + use std::convert::{TryFrom, TryInto}; + + use ruma_identifiers::RoomId; + + use crate::r0::filter::{LazyLoadOptions, RoomEventFilter}; + + #[test] + fn test_serialize_some_room_event_filter() { + let room_id = RoomId::try_from("!roomid:example.org").unwrap(); + let filter = RoomEventFilter { + lazy_load_options: LazyLoadOptions::Enabled { + include_redundant_members: true, + }, + rooms: Some(vec![room_id.clone()]), + not_rooms: vec!["room".into(), "room2".into(), "room3".into()], + not_types: vec!["type".into()], + ..Default::default() + }; + let req = Request { + room_id, + from: "token".into(), + to: Some("token2".into()), + dir: Direction::Backward, + limit: Some(js_int::UInt::min_value()), + filter: Some(filter), + }; + + let request: http::Request> = req.try_into().unwrap(); + assert_eq!( + "from=token&to=token2&dir=b&limit=0&filter=%7B%22not_types%22%3A%5B%22type%22%5D%2C%22not_rooms%22%3A%5B%22room%22%2C%22room2%22%2C%22room3%22%5D%2C%22rooms%22%3A%5B%22%21roomid%3Aexample.org%22%5D%2C%22lazy_load_members%22%3Atrue%2C%22include_redundant_members%22%3Atrue%7D", + request.uri().query().unwrap() + ); + } + + #[test] + fn test_serialize_none_room_event_filter() { + let room_id = RoomId::try_from("!roomid:example.org").unwrap(); + let req = Request { + room_id, + from: "token".into(), + to: Some("token2".into()), + dir: Direction::Backward, + limit: Some(js_int::UInt::min_value()), + filter: None, + }; + + let request: http::Request> = req.try_into().unwrap(); + assert_eq!( + "from=token&to=token2&dir=b&limit=0", + request.uri().query().unwrap(), + ); + } + + #[test] + fn test_serialize_default_room_event_filter() { + let room_id = RoomId::try_from("!roomid:example.org").unwrap(); + let req = Request { + room_id, + from: "token".into(), + to: Some("token2".into()), + dir: Direction::Backward, + limit: Some(js_int::UInt::min_value()), + filter: Some(RoomEventFilter::default()), + }; + + let request: http::Request> = req.try_into().unwrap(); + assert_eq!( + "from=token&to=token2&dir=b&limit=0&filter=%7B%7D", + request.uri().query().unwrap(), + ); + } +} diff --git a/src/r0/sync/sync_events.rs b/src/r0/sync/sync_events.rs index 455f26b3..c95e934b 100644 --- a/src/r0/sync/sync_events.rs +++ b/src/r0/sync/sync_events.rs @@ -122,39 +122,13 @@ pub enum Filter { // FilterDefinition is the first variant, JSON decoding is attempted first which is almost // functionally equivalent to looking at whether the first symbol is a '{' as the spec says. // (there are probably some corner cases like leading whitespace) - #[serde(with = "filter_def_serde")] + #[serde(with = "crate::serde::json_string")] /// A complete filter definition serialized to JSON. FilterDefinition(FilterDefinition), /// The ID of a filter saved on the server. FilterId(String), } -/// Serialization and deserialization logic for filter definitions. -mod filter_def_serde { - use serde::{de::Error as _, ser::Error as _, Deserialize, Deserializer, Serializer}; - - use crate::r0::filter::FilterDefinition; - - /// Serialization logic for filter definitions. - pub fn serialize(filter_def: &FilterDefinition, serializer: S) -> Result - where - S: Serializer, - { - let string = serde_json::to_string(filter_def).map_err(S::Error::custom)?; - serializer.serialize_str(&string) - } - - /// Deserialization logic for filter definitions. - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let filter_str = <&str>::deserialize(deserializer)?; - - serde_json::from_str(filter_str).map_err(D::Error::custom) - } -} - /// Updates to rooms. #[derive(Clone, Debug, Serialize, Outgoing)] pub struct Rooms { diff --git a/src/serde.rs b/src/serde.rs index 6ca75b9b..e89573d3 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,6 +1,7 @@ //! Modules to hold functions for de-/serializing remote types pub mod duration; +pub mod json_string; pub fn is_default(val: &T) -> bool { val == &T::default() diff --git a/src/serde/json_string.rs b/src/serde/json_string.rs new file mode 100644 index 00000000..e5fabe70 --- /dev/null +++ b/src/serde/json_string.rs @@ -0,0 +1,24 @@ +//! De-/serialization functions to and from json strings, allows the type to be used as a query string. + +use serde::{ + de::{Deserialize, DeserializeOwned, Deserializer, Error as _}, + ser::{Error as _, Serialize, Serializer}, +}; + +pub fn serialize(filter: T, serializer: S) -> Result +where + T: Serialize, + S: Serializer, +{ + let json = serde_json::to_string(&filter).map_err(S::Error::custom)?; + serializer.serialize_str(&json) +} + +pub fn deserialize<'de, T, D>(deserializer: D) -> Result +where + T: DeserializeOwned, + D: Deserializer<'de>, +{ + let s = String::deserialize(deserializer)?; + serde_json::from_str(&s).map_err(D::Error::custom) +}