diff --git a/crates/ruma-common/src/power_levels.rs b/crates/ruma-common/src/power_levels.rs index 67bbf4d7..7c8b0a06 100644 --- a/crates/ruma-common/src/power_levels.rs +++ b/crates/ruma-common/src/power_levels.rs @@ -13,7 +13,10 @@ pub struct NotificationPowerLevels { /// /// If you activate the `compat` feature, deserialization will work for stringified /// integers too. - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default = "default_power_level")] pub room: Int, } diff --git a/crates/ruma-events/src/room/power_levels.rs b/crates/ruma-events/src/room/power_levels.rs index 973f68cc..a04100fb 100644 --- a/crates/ruma-events/src/room/power_levels.rs +++ b/crates/ruma-events/src/room/power_levels.rs @@ -25,7 +25,10 @@ pub struct RoomPowerLevelsEventContent { /// /// If you activate the `compat` feature, deserialization will work for stringified /// integers too. - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default = "default_power_level", skip_serializing_if = "is_default_power_level")] #[ruma_event(skip_redaction)] pub ban: Int, @@ -38,7 +41,7 @@ pub struct RoomPowerLevelsEventContent { /// integers too. #[cfg_attr( feature = "compat", - serde(deserialize_with = "ruma_serde::btreemap_int_or_string_to_int_values") + serde(deserialize_with = "ruma_serde::btreemap_deserialize_v1_powerlevel_values") )] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[ruma_event(skip_redaction)] @@ -48,7 +51,10 @@ pub struct RoomPowerLevelsEventContent { /// /// If you activate the `compat` feature, deserialization will work for stringified /// integers too. - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default, skip_serializing_if = "ruma_serde::is_default")] #[ruma_event(skip_redaction)] pub events_default: Int, @@ -57,7 +63,10 @@ pub struct RoomPowerLevelsEventContent { /// /// If you activate the `compat` feature, deserialization will work for stringified /// integers too. - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default = "default_power_level", skip_serializing_if = "is_default_power_level")] pub invite: Int, @@ -65,7 +74,10 @@ pub struct RoomPowerLevelsEventContent { /// /// If you activate the `compat` feature, deserialization will work for stringified /// integers too. - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default = "default_power_level", skip_serializing_if = "is_default_power_level")] #[ruma_event(skip_redaction)] pub kick: Int, @@ -74,7 +86,10 @@ pub struct RoomPowerLevelsEventContent { /// /// If you activate the `compat` feature, deserialization will work for stringified /// integers too. - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default = "default_power_level", skip_serializing_if = "is_default_power_level")] #[ruma_event(skip_redaction)] pub redact: Int, @@ -83,7 +98,10 @@ pub struct RoomPowerLevelsEventContent { /// /// If you activate the `compat` feature, deserialization will work for stringified /// integers too. - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default = "default_power_level", skip_serializing_if = "is_default_power_level")] #[ruma_event(skip_redaction)] pub state_default: Int, @@ -96,7 +114,7 @@ pub struct RoomPowerLevelsEventContent { /// integers too. #[cfg_attr( feature = "compat", - serde(deserialize_with = "ruma_serde::btreemap_int_or_string_to_int_values") + serde(deserialize_with = "ruma_serde::btreemap_deserialize_v1_powerlevel_values") )] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] #[ruma_event(skip_redaction)] @@ -106,7 +124,10 @@ pub struct RoomPowerLevelsEventContent { /// /// If you activate the `compat` feature, deserialization will work for stringified /// integers too. - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default, skip_serializing_if = "ruma_serde::is_default")] #[ruma_event(skip_redaction)] pub users_default: Int, diff --git a/crates/ruma-serde/src/lib.rs b/crates/ruma-serde/src/lib.rs index f7081ea5..520253c4 100644 --- a/crates/ruma-serde/src/lib.rs +++ b/crates/ruma-serde/src/lib.rs @@ -33,7 +33,7 @@ pub use self::{ empty::vec_as_map_of_empty, raw::Raw, strings::{ - btreemap_int_or_string_to_int_values, empty_string_as_none, int_or_string_to_int, + btreemap_deserialize_v1_powerlevel_values, deserialize_v1_powerlevel, empty_string_as_none, none_as_empty_string, }, }; diff --git a/crates/ruma-serde/src/strings.rs b/crates/ruma-serde/src/strings.rs index 85f584f7..d3cfc0b6 100644 --- a/crates/ruma-serde/src/strings.rs +++ b/crates/ruma-serde/src/strings.rs @@ -1,6 +1,6 @@ use std::{collections::BTreeMap, convert::TryInto, fmt, marker::PhantomData}; -use js_int::Int; +use js_int::{Int, UInt}; use serde::{ de::{self, Deserializer, IntoDeserializer as _, MapAccess, Visitor}, ser::Serializer, @@ -52,8 +52,8 @@ where /// Take either an integer number or a string and deserialize to an integer number. /// /// To be used like this: -/// `#[serde(deserialize_with = "int_or_string_to_int")]` -pub fn int_or_string_to_int<'de, D>(de: D) -> Result +/// `#[serde(deserialize_with = "deserialize_v1_powerlevel")]` +pub fn deserialize_v1_powerlevel<'de, D>(de: D) -> Result where D: Deserializer<'de>, { @@ -107,7 +107,12 @@ where } fn visit_str(self, v: &str) -> Result { - v.parse().map_err(E::custom) + let trimmed = v.trim(); + + match trimmed.strip_prefix('+') { + Some(without) => without.parse::().map(|u| u.into()).map_err(E::custom), + None => trimmed.parse().map_err(E::custom), + } } } @@ -118,8 +123,10 @@ where /// those to integer numbers. /// /// To be used like this: -/// `#[serde(deserialize_with = "btreemap_int_or_string_to_int_values")]` -pub fn btreemap_int_or_string_to_int_values<'de, D, T>(de: D) -> Result, D::Error> +/// `#[serde(deserialize_with = "btreemap_deserialize_v1_powerlevel_values")]` +pub fn btreemap_deserialize_v1_powerlevel_values<'de, D, T>( + de: D, +) -> Result, D::Error> where D: Deserializer<'de>, T: Deserialize<'de> + Ord, @@ -132,7 +139,7 @@ where where D: Deserializer<'de>, { - int_or_string_to_int(deserializer).map(IntWrap) + deserialize_v1_powerlevel(deserializer).map(IntWrap) } } @@ -176,16 +183,16 @@ mod tests { use matches::assert_matches; use serde::Deserialize; - use super::int_or_string_to_int; + use super::deserialize_v1_powerlevel; + + #[derive(Debug, Deserialize)] + struct Test { + #[serde(deserialize_with = "deserialize_v1_powerlevel")] + num: Int, + } #[test] fn int_or_string() -> serde_json::Result<()> { - #[derive(Debug, Deserialize)] - struct Test { - #[serde(deserialize_with = "int_or_string_to_int")] - num: Int, - } - assert_matches!( serde_json::from_value::(serde_json::json!({ "num": "0" }))?, Test { num } if num == int!(0) @@ -193,4 +200,24 @@ mod tests { Ok(()) } + + #[test] + fn weird_plus_string() -> serde_json::Result<()> { + assert_matches!( + serde_json::from_value::(serde_json::json!({ "num": " +0000000001000 " }))?, + Test { num } if num == int!(1000) + ); + + Ok(()) + } + + #[test] + fn weird_minus_string() -> serde_json::Result<()> { + assert_matches!( + serde_json::from_value::(serde_json::json!({ "num": " \n\n-0000000000000001000 " }))?, + Test { num } if num == int!(-1000) + ); + + Ok(()) + } } diff --git a/crates/ruma-state-res/src/lib.rs b/crates/ruma-state-res/src/lib.rs index c042c033..90df3c8e 100644 --- a/crates/ruma-state-res/src/lib.rs +++ b/crates/ruma-state-res/src/lib.rs @@ -339,12 +339,15 @@ where struct PowerLevelsContentFields { #[cfg_attr( feature = "compat", - serde(deserialize_with = "ruma_serde::btreemap_int_or_string_to_int_values") + serde(deserialize_with = "ruma_serde::btreemap_deserialize_v1_powerlevel_values") )] #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] users: BTreeMap, Int>, - #[cfg_attr(feature = "compat", serde(deserialize_with = "ruma_serde::int_or_string_to_int"))] + #[cfg_attr( + feature = "compat", + serde(deserialize_with = "ruma_serde::deserialize_v1_powerlevel") + )] #[serde(default, skip_serializing_if = "ruma_serde::is_default")] users_default: Int, }