diff --git a/crates/ruma-common/CHANGELOG.md b/crates/ruma-common/CHANGELOG.md index b1c6270b..d7bb4b2d 100644 --- a/crates/ruma-common/CHANGELOG.md +++ b/crates/ruma-common/CHANGELOG.md @@ -26,6 +26,8 @@ Breaking changes: * `FromHttpResponseError::Server` now contains `E` instead of `ServerError` * `ServerError` has been removed * `MatrixError` is now an enum with the `Json` variant containing the previous fields +* Change the `ignored_users` field of `IgnoredUserListEventContent` to a map of empty structs, to + allow eventual fields to be added, as intended by the spec Improvements: diff --git a/crates/ruma-common/src/events/ignored_user_list.rs b/crates/ruma-common/src/events/ignored_user_list.rs index 5a688a64..f08d4955 100644 --- a/crates/ruma-common/src/events/ignored_user_list.rs +++ b/crates/ruma-common/src/events/ignored_user_list.rs @@ -2,6 +2,8 @@ //! //! [`m.ignored_user_list`]: https://spec.matrix.org/v1.4/client-server-api/#mignored_user_list +use std::collections::BTreeMap; + use ruma_macros::EventContent; use serde::{Deserialize, Serialize}; @@ -14,103 +16,32 @@ use crate::OwnedUserId; #[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] #[ruma_event(type = "m.ignored_user_list", kind = GlobalAccountData)] pub struct IgnoredUserListEventContent { - /// A list of users to ignore. - #[serde(with = "vec_as_map_of_empty")] - pub ignored_users: Vec, + /// A map of users to ignore. + /// + /// As [`IgnoredUser`] is currently empty, only the user IDs are useful and + /// can be accessed with the `.keys()` and `into_keys()` iterators. + pub ignored_users: BTreeMap, } impl IgnoredUserListEventContent { - /// Creates a new `IgnoredUserListEventContent` from the given user IDs. - pub fn new(ignored_users: Vec) -> Self { + /// Creates a new `IgnoredUserListEventContent` from the given map of ignored user. + pub fn new(ignored_users: BTreeMap) -> Self { Self { ignored_users } } -} -mod vec_as_map_of_empty { - use std::{fmt, marker::PhantomData}; - - use serde::{ - de::{self, Deserialize, Deserializer}, - ser::{SerializeMap, Serializer}, - Serialize, - }; - - /// Serialize the given `Vec` as a map of `T => Empty`. - #[allow(clippy::ptr_arg)] - pub fn serialize(vec: &Vec, serializer: S) -> Result - where - S: Serializer, - T: Serialize + Eq + Ord, - { - let mut map = serializer.serialize_map(Some(vec.len()))?; - for item in vec { - map.serialize_entry(item, &Empty {})?; - } - map.end() - } - - /// Deserialize an object and return the keys as a `Vec`. - pub fn deserialize<'de, D, T>(deserializer: D) -> Result, D::Error> - where - D: Deserializer<'de>, - T: Deserialize<'de> + Eq + Ord, - { - struct MapOfEmptyVisitor(PhantomData); - impl<'de, T> de::Visitor<'de> for MapOfEmptyVisitor - where - T: Deserialize<'de>, - { - type Value = Vec; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "an object/map") - } - - fn visit_map(self, mut map: A) -> Result - where - A: de::MapAccess<'de>, - { - let mut items = Vec::with_capacity(map.size_hint().unwrap_or(0)); - while let Some((item, _)) = map.next_entry::()? { - items.push(item); - } - Ok(items) - } - } - - deserializer.deserialize_map(MapOfEmptyVisitor(PhantomData)) - } - - #[derive(Clone, Debug, Serialize)] - struct Empty {} - - impl<'de> Deserialize<'de> for Empty { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - struct EmptyMapVisitor; - - impl<'de> de::Visitor<'de> for EmptyMapVisitor { - type Value = Empty; - - fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "an object/map") - } - - fn visit_map(self, _map: A) -> Result - where - A: de::MapAccess<'de>, - { - Ok(Empty {}) - } - } - - deserializer.deserialize_map(EmptyMapVisitor) - } + /// Creates a new `IgnoredUserListEventContent` from the given list of users. + pub fn users(ignored_users: impl IntoIterator) -> Self { + Self::new(ignored_users.into_iter().map(|id| (id, IgnoredUser {})).collect()) } } +/// Details about an ignored user. +/// +/// This is currently empty. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[cfg_attr(not(feature = "unstable-exhaustive-types"), non_exhaustive)] +pub struct IgnoredUser {} + #[cfg(test)] mod tests { use assert_matches::assert_matches; @@ -125,9 +56,9 @@ mod tests { #[test] fn serialization() { let ignored_user_list_event = GlobalAccountDataEvent { - content: IgnoredUserListEventContent { - ignored_users: vec![user_id!("@carl:example.com").to_owned()], - }, + content: IgnoredUserListEventContent::users(vec![ + user_id!("@carl:example.com").to_owned() + ]), }; let json = json!({ @@ -157,6 +88,9 @@ mod tests { from_json_value::(json), Ok(AnyGlobalAccountDataEvent::IgnoredUserList(ev)) => ev ); - assert_eq!(ev.content.ignored_users, vec![user_id!("@carl:example.com")]); + assert_eq!( + ev.content.ignored_users.keys().collect::>(), + vec![user_id!("@carl:example.com")] + ); } }