ruma_common/serde/
strings.rs

1use std::{collections::BTreeMap, fmt, marker::PhantomData};
2
3use js_int::{Int, UInt};
4use serde::{
5    de::{self, Deserializer, IntoDeserializer as _, MapAccess, Visitor},
6    ser::Serializer,
7    Deserialize, Serialize,
8};
9
10/// Serde deserialization decorator to map empty Strings to None,
11/// and forward non-empty Strings to the Deserialize implementation for T.
12///
13/// Useful for the typical
14/// "A room with an X event with an absent, null, or empty Y field
15/// should be treated the same as a room with no such event."
16/// formulation in the spec.
17///
18/// To be used like this:
19/// `#[serde(default, deserialize_with = "empty_string_as_none")]`
20/// Relevant serde issue: <https://github.com/serde-rs/serde/issues/1425>
21pub fn empty_string_as_none<'de, D, T>(de: D) -> Result<Option<T>, D::Error>
22where
23    D: Deserializer<'de>,
24    T: Deserialize<'de>,
25{
26    let opt = Option::<String>::deserialize(de)?;
27    match opt.as_deref() {
28        None | Some("") => Ok(None),
29        // If T = String, like in m.room.name, the second deserialize is actually superfluous.
30        // TODO: optimize that somehow?
31        Some(s) => T::deserialize(s.into_deserializer()).map(Some),
32    }
33}
34
35/// Serde serializiation decorator to map `None` to an empty `String`,
36/// and forward `Some`s to the `Serialize` implementation for `T`.
37///
38/// To be used like this:
39/// `#[serde(serialize_with = "empty_string_as_none")]`
40pub fn none_as_empty_string<T: Serialize, S>(
41    value: &Option<T>,
42    serializer: S,
43) -> Result<S::Ok, S::Error>
44where
45    S: Serializer,
46{
47    match value {
48        Some(x) => x.serialize(serializer),
49        None => serializer.serialize_str(""),
50    }
51}
52
53/// Take either a floating point number or a string and deserialize to an floating-point number.
54///
55/// To be used like this:
56/// `#[serde(deserialize_with = "deserialize_as_number_or_string")]`
57pub fn deserialize_as_number_or_string<'de, D>(de: D) -> Result<f64, D::Error>
58where
59    D: Deserializer<'de>,
60{
61    struct F64OrStringVisitor;
62
63    impl Visitor<'_> for F64OrStringVisitor {
64        type Value = f64;
65
66        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
67            formatter.write_str("a double or a string")
68        }
69
70        fn visit_f32<E>(self, v: f32) -> Result<Self::Value, E>
71        where
72            E: de::Error,
73        {
74            Ok(v.into())
75        }
76
77        fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
78        where
79            E: de::Error,
80        {
81            Ok(v)
82        }
83
84        fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
85        where
86            E: de::Error,
87        {
88            if v <= (f64::MAX as u64) {
89                Ok(v as f64)
90            } else {
91                Err(E::custom("u64 is too large to fit into a f64"))
92            }
93        }
94
95        fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
96        where
97            E: de::Error,
98        {
99            if v <= (f64::MAX as i64) && v >= (f64::MIN as i64) {
100                Ok(v as f64)
101            } else {
102                Err(E::custom("i64 is too large to fit into a f64"))
103            }
104        }
105
106        fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
107            v.parse().map_err(E::custom)
108        }
109    }
110
111    de.deserialize_any(F64OrStringVisitor)
112}
113
114#[derive(Deserialize)]
115struct NumberOrStringWrapper(#[serde(deserialize_with = "deserialize_as_number_or_string")] f64);
116
117/// Deserializes an `Option<f64>` from an encoded f64 or string or integer (i64 or u64).
118pub fn deserialize_as_optional_number_or_string<'de, D>(
119    deserializer: D,
120) -> Result<Option<f64>, D::Error>
121where
122    D: Deserializer<'de>,
123{
124    Ok(Option::<NumberOrStringWrapper>::deserialize(deserializer)?.map(|w| w.0))
125}
126
127/// Take either an integer number or a string and deserialize to an integer number.
128///
129/// To be used like this:
130/// `#[serde(deserialize_with = "deserialize_v1_powerlevel")]`
131pub fn deserialize_v1_powerlevel<'de, D>(de: D) -> Result<Int, D::Error>
132where
133    D: Deserializer<'de>,
134{
135    struct IntOrStringVisitor;
136
137    impl Visitor<'_> for IntOrStringVisitor {
138        type Value = Int;
139
140        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
141            formatter.write_str("an integer or a string")
142        }
143
144        fn visit_i8<E: de::Error>(self, v: i8) -> Result<Self::Value, E> {
145            Ok(v.into())
146        }
147
148        fn visit_i16<E: de::Error>(self, v: i16) -> Result<Self::Value, E> {
149            Ok(v.into())
150        }
151
152        fn visit_i32<E: de::Error>(self, v: i32) -> Result<Self::Value, E> {
153            Ok(v.into())
154        }
155
156        fn visit_i64<E: de::Error>(self, v: i64) -> Result<Self::Value, E> {
157            v.try_into().map_err(E::custom)
158        }
159
160        fn visit_i128<E: de::Error>(self, v: i128) -> Result<Self::Value, E> {
161            v.try_into().map_err(E::custom)
162        }
163
164        fn visit_u8<E: de::Error>(self, v: u8) -> Result<Self::Value, E> {
165            Ok(v.into())
166        }
167
168        fn visit_u16<E: de::Error>(self, v: u16) -> Result<Self::Value, E> {
169            Ok(v.into())
170        }
171
172        fn visit_u32<E: de::Error>(self, v: u32) -> Result<Self::Value, E> {
173            Ok(v.into())
174        }
175
176        fn visit_u64<E: de::Error>(self, v: u64) -> Result<Self::Value, E> {
177            v.try_into().map_err(E::custom)
178        }
179
180        fn visit_u128<E: de::Error>(self, v: u128) -> Result<Self::Value, E> {
181            v.try_into().map_err(E::custom)
182        }
183
184        fn visit_str<E: de::Error>(self, v: &str) -> Result<Self::Value, E> {
185            let trimmed = v.trim();
186
187            match trimmed.strip_prefix('+') {
188                Some(without) => without.parse::<UInt>().map(|u| u.into()).map_err(E::custom),
189                None => trimmed.parse().map_err(E::custom),
190            }
191        }
192    }
193
194    de.deserialize_any(IntOrStringVisitor)
195}
196
197/// Take a BTreeMap with values of either an integer number or a string and deserialize
198/// those to integer numbers.
199///
200/// To be used like this:
201/// `#[serde(deserialize_with = "btreemap_deserialize_v1_powerlevel_values")]`
202pub fn btreemap_deserialize_v1_powerlevel_values<'de, D, T>(
203    de: D,
204) -> Result<BTreeMap<T, Int>, D::Error>
205where
206    D: Deserializer<'de>,
207    T: Deserialize<'de> + Ord,
208{
209    #[repr(transparent)]
210    struct IntWrap(Int);
211
212    impl<'de> Deserialize<'de> for IntWrap {
213        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
214        where
215            D: Deserializer<'de>,
216        {
217            deserialize_v1_powerlevel(deserializer).map(IntWrap)
218        }
219    }
220
221    struct IntMapVisitor<T> {
222        _phantom: PhantomData<T>,
223    }
224
225    impl<T> IntMapVisitor<T> {
226        fn new() -> Self {
227            Self { _phantom: PhantomData }
228        }
229    }
230
231    impl<'de, T> Visitor<'de> for IntMapVisitor<T>
232    where
233        T: Deserialize<'de> + Ord,
234    {
235        type Value = BTreeMap<T, Int>;
236
237        fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
238            formatter.write_str("a map with integers or strings as values")
239        }
240
241        fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
242            let mut res = BTreeMap::new();
243
244            while let Some((k, IntWrap(v))) = map.next_entry()? {
245                res.insert(k, v);
246            }
247
248            Ok(res)
249        }
250    }
251
252    de.deserialize_map(IntMapVisitor::new())
253}
254
255#[cfg(test)]
256mod tests {
257    use js_int::{int, Int};
258    use serde::Deserialize;
259
260    use super::deserialize_v1_powerlevel;
261
262    #[derive(Debug, Deserialize)]
263    struct Test {
264        #[serde(deserialize_with = "deserialize_v1_powerlevel")]
265        num: Int,
266    }
267
268    #[test]
269    fn int_or_string() {
270        let test = serde_json::from_value::<Test>(serde_json::json!({ "num": "0" })).unwrap();
271        assert_eq!(test.num, int!(0));
272    }
273
274    #[test]
275    fn weird_plus_string() {
276        let test =
277            serde_json::from_value::<Test>(serde_json::json!({ "num": "  +0000000001000   " }))
278                .unwrap();
279        assert_eq!(test.num, int!(1000));
280    }
281
282    #[test]
283    fn weird_minus_string() {
284        let test = serde_json::from_value::<Test>(
285            serde_json::json!({ "num": "  \n\n-0000000000000001000   " }),
286        )
287        .unwrap();
288        assert_eq!(test.num, int!(-1000));
289    }
290}