events: allow deserialize a m.tag
's order
as an integer (#1767)
Some servers use an integer to represent e.g. `1` for the order, instead of the double representation (that would be `1.` or `1.0)`. This makes it possible to parse such values as integers too, since they're technically not double. Implementing `visit_u64` and `visit_i64` covers all the smaller sizes too, so I've implemented only that here.
This commit is contained in:
parent
becc4ac0b3
commit
ccc0a64ba3
@ -11,6 +11,8 @@ Breaking changes:
|
|||||||
`m.room.power_levels`.
|
`m.room.power_levels`.
|
||||||
- Add support for endpoints that take an optional authentication
|
- Add support for endpoints that take an optional authentication
|
||||||
- Add support for endpoints that require authentication for appservices
|
- Add support for endpoints that require authentication for appservices
|
||||||
|
- `deserialize_as_f64_or_string` has been extended to also support parsing integers, and renamed to
|
||||||
|
`deserialize_as_number_or_string` to reflect that.
|
||||||
|
|
||||||
Improvements:
|
Improvements:
|
||||||
|
|
||||||
|
@ -26,8 +26,8 @@ pub use self::{
|
|||||||
cow::deserialize_cow_str,
|
cow::deserialize_cow_str,
|
||||||
raw::Raw,
|
raw::Raw,
|
||||||
strings::{
|
strings::{
|
||||||
btreemap_deserialize_v1_powerlevel_values, deserialize_as_f64_or_string,
|
btreemap_deserialize_v1_powerlevel_values, deserialize_as_number_or_string,
|
||||||
deserialize_as_optional_f64_or_string, deserialize_v1_powerlevel, empty_string_as_none,
|
deserialize_as_optional_number_or_string, deserialize_v1_powerlevel, empty_string_as_none,
|
||||||
none_as_empty_string,
|
none_as_empty_string,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -52,8 +52,8 @@ where
|
|||||||
/// Take either a floating point number or a string and deserialize to an floating-point number.
|
/// Take either a floating point number or a string and deserialize to an floating-point number.
|
||||||
///
|
///
|
||||||
/// To be used like this:
|
/// To be used like this:
|
||||||
/// `#[serde(deserialize_with = "deserialize_as_f64_or_string")]`
|
/// `#[serde(deserialize_with = "deserialize_as_number_or_string")]`
|
||||||
pub fn deserialize_as_f64_or_string<'de, D>(de: D) -> Result<f64, D::Error>
|
pub fn deserialize_as_number_or_string<'de, D>(de: D) -> Result<f64, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
@ -80,6 +80,28 @@ where
|
|||||||
Ok(v)
|
Ok(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if v <= (f64::MAX as u64) {
|
||||||
|
Ok(v as f64)
|
||||||
|
} else {
|
||||||
|
Err(E::custom("u64 is too large to fit into a f64"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
if v <= (f64::MAX as i64) && v >= (f64::MIN as i64) {
|
||||||
|
Ok(v as f64)
|
||||||
|
} else {
|
||||||
|
Err(E::custom("i64 is too large to fit into a f64"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
|
fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
|
||||||
v.parse().map_err(E::custom)
|
v.parse().map_err(E::custom)
|
||||||
}
|
}
|
||||||
@ -89,16 +111,16 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct F64OrStringWrapper(#[serde(deserialize_with = "deserialize_as_f64_or_string")] f64);
|
struct NumberOrStringWrapper(#[serde(deserialize_with = "deserialize_as_number_or_string")] f64);
|
||||||
|
|
||||||
/// Deserializes an `Option<f64>` as encoded as a f64 or a string.
|
/// Deserializes an `Option<f64>` from an encoded f64 or string or integer (i64 or u64).
|
||||||
pub fn deserialize_as_optional_f64_or_string<'de, D>(
|
pub fn deserialize_as_optional_number_or_string<'de, D>(
|
||||||
deserializer: D,
|
deserializer: D,
|
||||||
) -> Result<Option<f64>, D::Error>
|
) -> Result<Option<f64>, D::Error>
|
||||||
where
|
where
|
||||||
D: Deserializer<'de>,
|
D: Deserializer<'de>,
|
||||||
{
|
{
|
||||||
Ok(Option::<F64OrStringWrapper>::deserialize(deserializer)?.map(|w| w.0))
|
Ok(Option::<NumberOrStringWrapper>::deserialize(deserializer)?.map(|w| w.0))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Take either an integer number or a string and deserialize to an integer number.
|
/// Take either an integer number or a string and deserialize to an integer number.
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
use std::{collections::BTreeMap, error::Error, fmt, str::FromStr};
|
use std::{collections::BTreeMap, error::Error, fmt, str::FromStr};
|
||||||
|
|
||||||
#[cfg(feature = "compat-tag-info")]
|
#[cfg(feature = "compat-tag-info")]
|
||||||
use ruma_common::serde::deserialize_as_optional_f64_or_string;
|
use ruma_common::serde::deserialize_as_optional_number_or_string;
|
||||||
use ruma_common::serde::deserialize_cow_str;
|
use ruma_common::serde::deserialize_cow_str;
|
||||||
use ruma_macros::EventContent;
|
use ruma_macros::EventContent;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -180,7 +180,7 @@ pub struct TagInfo {
|
|||||||
#[serde(skip_serializing_if = "Option::is_none")]
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
feature = "compat-tag-info",
|
feature = "compat-tag-info",
|
||||||
serde(default, deserialize_with = "deserialize_as_optional_f64_or_string")
|
serde(default, deserialize_with = "deserialize_as_optional_number_or_string")
|
||||||
)]
|
)]
|
||||||
pub order: Option<f64>,
|
pub order: Option<f64>,
|
||||||
}
|
}
|
||||||
@ -233,6 +233,9 @@ mod tests {
|
|||||||
let json = json!({ "order": null });
|
let json = json!({ "order": null });
|
||||||
assert_eq!(from_json_value::<TagInfo>(json).unwrap(), TagInfo::default());
|
assert_eq!(from_json_value::<TagInfo>(json).unwrap(), TagInfo::default());
|
||||||
|
|
||||||
|
let json = json!({ "order": 1 });
|
||||||
|
assert_eq!(from_json_value::<TagInfo>(json).unwrap(), TagInfo { order: Some(1.) });
|
||||||
|
|
||||||
let json = json!({ "order": 0.42 });
|
let json = json!({ "order": 0.42 });
|
||||||
assert_eq!(from_json_value::<TagInfo>(json).unwrap(), TagInfo { order: Some(0.42) });
|
assert_eq!(from_json_value::<TagInfo>(json).unwrap(), TagInfo { order: Some(0.42) });
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user