diff --git a/Cargo.toml b/Cargo.toml index f9888959..dab852c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,6 +25,9 @@ features = ["serde"] version = "1.0.101" features = ["derive"] +[dev-dependencies] +maplit = "1.0.2" + [workspace] members = [ "ruma-events-macros", diff --git a/src/room/power_levels.rs b/src/room/power_levels.rs index 0692ed5f..1497b386 100644 --- a/src/room/power_levels.rs +++ b/src/room/power_levels.rs @@ -42,49 +42,49 @@ pub struct PowerLevelsEvent { #[derive(Clone, Debug, PartialEq, Serialize)] pub struct PowerLevelsEventContent { /// The level required to ban a user. - #[serde(default = "default_power_level")] + #[serde(skip_serializing_if = "is_default_power_level")] pub ban: Int, /// The level required to send specific event types. /// /// This is a mapping from event type to power level required. - #[serde(default, skip_serializing_if = "HashMap::is_empty")] + #[serde(skip_serializing_if = "HashMap::is_empty")] pub events: HashMap, /// The default level required to send message events. - #[serde(default)] + #[serde(skip_serializing_if = "is_power_level_zero")] pub events_default: Int, /// The level required to invite a user. - #[serde(default = "default_power_level")] + #[serde(skip_serializing_if = "is_default_power_level")] pub invite: Int, /// The level required to kick a user. - #[serde(default = "default_power_level")] + #[serde(skip_serializing_if = "is_default_power_level")] pub kick: Int, /// The level required to redact an event. - #[serde(default = "default_power_level")] + #[serde(skip_serializing_if = "is_default_power_level")] pub redact: Int, /// The default level required to send state events. - #[serde(default = "default_power_level")] + #[serde(skip_serializing_if = "is_default_power_level")] pub state_default: Int, /// The power levels for specific users. /// /// This is a mapping from `user_id` to power level for that user. - #[serde(default, skip_serializing_if = "HashMap::is_empty")] + #[serde(skip_serializing_if = "HashMap::is_empty")] pub users: HashMap, /// The default power level for every user in the room. - #[serde(default)] + #[serde(skip_serializing_if = "is_power_level_zero")] pub users_default: Int, /// The power level requirements for specific notification types. /// /// This is a mapping from `key` to power level for that notifications key. - #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")] + #[serde(skip_serializing_if = "NotificationPowerLevels::is_default")] pub notifications: NotificationPowerLevels, } @@ -217,7 +217,7 @@ pub(crate) mod raw { /// The level required to send specific event types. /// /// This is a mapping from event type to power level required. - #[serde(default, skip_serializing_if = "HashMap::is_empty")] + #[serde(default)] pub events: HashMap, /// The default level required to send message events. @@ -243,7 +243,7 @@ pub(crate) mod raw { /// The power levels for specific users. /// /// This is a mapping from `user_id` to power level for that user. - #[serde(default, skip_serializing_if = "HashMap::is_empty")] + #[serde(default)] pub users: HashMap, /// The default power level for every user in the room. @@ -253,7 +253,7 @@ pub(crate) mod raw { /// The power level requirements for specific notification types. /// /// This is a mapping from `key` to power level for that notifications key. - #[serde(default, skip_serializing_if = "NotificationPowerLevels::is_default")] + #[serde(default)] pub notifications: NotificationPowerLevels, } } @@ -288,35 +288,51 @@ fn default_power_level() -> Int { Int::from(50) } +/// Used with #[serde(skip_serializing_if)] to omit default power levels. +#[allow(clippy::trivially_copy_pass_by_ref)] +fn is_default_power_level(l: &Int) -> bool { + *l == Int::from(50) +} + +/// Used with #[serde(skip_serializing_if)] to omit default power levels. +#[allow(clippy::trivially_copy_pass_by_ref)] +fn is_power_level_zero(l: &Int) -> bool { + *l == Int::from(0) +} + #[cfg(test)] mod tests { use std::{collections::HashMap, convert::TryFrom}; use js_int::{Int, UInt}; + use maplit::hashmap; use ruma_identifiers::{EventId, RoomId, UserId}; use serde_json::Value; - use super::{NotificationPowerLevels, PowerLevelsEvent, PowerLevelsEventContent}; + use super::{ + default_power_level, NotificationPowerLevels, PowerLevelsEvent, PowerLevelsEventContent, + }; + use crate::EventType; #[test] fn serialization_with_optional_fields_as_none() { - let default = Int::try_from(50).unwrap(); + let default = default_power_level(); let power_levels_event = PowerLevelsEvent { content: PowerLevelsEventContent { ban: default, events: HashMap::new(), - events_default: default, + events_default: Int::from(0), invite: default, kick: default, redact: default, state_default: default, users: HashMap::new(), - users_default: default, - notifications: NotificationPowerLevels { room: default }, + users_default: Int::from(0), + notifications: NotificationPowerLevels::default(), }, event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(), - origin_server_ts: UInt::try_from(1).unwrap(), + origin_server_ts: UInt::from(1u32), prev_content: None, room_id: None, unsigned: None, @@ -325,51 +341,62 @@ mod tests { }; let actual = serde_json::to_string(&power_levels_event).unwrap(); - let expected = r#"{"content":{"ban":50,"events":{},"events_default":50,"invite":50,"kick":50,"redact":50,"state_default":50,"users":{},"users_default":50,"notifications":{"room":50}},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"sender":"@carl:example.com","state_key":"","type":"m.room.power_levels"}"#; + let expected = r#"{"content":{},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"sender":"@carl:example.com","state_key":"","type":"m.room.power_levels"}"#; assert_eq!(actual, expected); } #[test] fn serialization_with_all_fields() { - let default = Int::try_from(50).unwrap(); - + let user = UserId::try_from("@carl:example.com").unwrap(); let power_levels_event = PowerLevelsEvent { content: PowerLevelsEventContent { - ban: default, - events: HashMap::new(), - events_default: default, - invite: default, - kick: default, - redact: default, - state_default: default, - users: HashMap::new(), - users_default: default, - notifications: NotificationPowerLevels { room: default }, + ban: Int::from(23), + events: hashmap! { + EventType::Dummy => Int::from(23) + }, + events_default: Int::from(23), + invite: Int::from(23), + kick: Int::from(23), + redact: Int::from(23), + state_default: Int::from(23), + users: hashmap! { + user.clone() => Int::from(23) + }, + users_default: Int::from(23), + notifications: NotificationPowerLevels { + room: Int::from(23), + }, }, event_id: EventId::try_from("$h29iv0s8:example.com").unwrap(), - origin_server_ts: UInt::try_from(1).unwrap(), + origin_server_ts: UInt::from(1u32), prev_content: Some(PowerLevelsEventContent { // Make just one field different so we at least know they're two different objects. - ban: Int::try_from(75).unwrap(), - events: HashMap::new(), - events_default: default, - invite: default, - kick: default, - redact: default, - state_default: default, - users: HashMap::new(), - users_default: default, - notifications: NotificationPowerLevels { room: default }, + ban: Int::from(42), + events: hashmap! { + EventType::Dummy => Int::from(42) + }, + events_default: Int::from(42), + invite: Int::from(42), + kick: Int::from(42), + redact: Int::from(42), + state_default: Int::from(42), + users: hashmap! { + user.clone() => Int::from(42) + }, + users_default: Int::from(42), + notifications: NotificationPowerLevels { + room: Int::from(42), + }, }), room_id: Some(RoomId::try_from("!n8f893n9:example.com").unwrap()), unsigned: Some(serde_json::from_str::(r#"{"foo":"bar"}"#).unwrap()), - sender: UserId::try_from("@carl:example.com").unwrap(), + sender: user, state_key: "".to_string(), }; let actual = serde_json::to_string(&power_levels_event).unwrap(); - let expected = r#"{"content":{"ban":50,"events":{},"events_default":50,"invite":50,"kick":50,"redact":50,"state_default":50,"users":{},"users_default":50,"notifications":{"room":50}},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"prev_content":{"ban":75,"events":{},"events_default":50,"invite":50,"kick":50,"redact":50,"state_default":50,"users":{},"users_default":50,"notifications":{"room":50}},"room_id":"!n8f893n9:example.com","sender":"@carl:example.com","state_key":"","type":"m.room.power_levels","unsigned":{"foo":"bar"}}"#; + let expected = r#"{"content":{"ban":23,"events":{"m.dummy":23},"events_default":23,"invite":23,"kick":23,"redact":23,"state_default":23,"users":{"@carl:example.com":23},"users_default":23,"notifications":{"room":23}},"event_id":"$h29iv0s8:example.com","origin_server_ts":1,"prev_content":{"ban":42,"events":{"m.dummy":42},"events_default":42,"invite":42,"kick":42,"redact":42,"state_default":42,"users":{"@carl:example.com":42},"users_default":42,"notifications":{"room":42}},"room_id":"!n8f893n9:example.com","sender":"@carl:example.com","state_key":"","type":"m.room.power_levels","unsigned":{"foo":"bar"}}"#; assert_eq!(actual, expected); }