ruma_serde/canonical_json/
value.rs

1use std::{
2    collections::BTreeMap,
3    convert::{TryFrom, TryInto},
4    fmt,
5};
6
7use js_int::Int;
8use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
9use serde_json::{to_string as to_json_string, Value as JsonValue};
10
11use super::Error;
12
13/// The inner type of `CanonicalJsonValue::Object`.
14pub type Object = BTreeMap<String, CanonicalJsonValue>;
15
16/// Represents a canonical JSON value as per the Matrix specification.
17#[derive(Clone, Eq, PartialEq)]
18#[allow(clippy::exhaustive_enums)]
19pub enum CanonicalJsonValue {
20    /// Represents a JSON null value.
21    ///
22    /// ```
23    /// # use serde_json::json;
24    /// # use std::convert::TryInto;
25    /// # use ruma_serde::CanonicalJsonValue;
26    /// let v: CanonicalJsonValue = json!(null).try_into().unwrap();
27    /// ```
28    Null,
29
30    /// Represents a JSON boolean.
31    ///
32    /// ```
33    /// # use serde_json::json;
34    /// # use std::convert::TryInto;
35    /// # use ruma_serde::CanonicalJsonValue;
36    /// let v: CanonicalJsonValue = json!(true).try_into().unwrap();
37    /// ```
38    Bool(bool),
39
40    /// Represents a JSON integer.
41    ///
42    /// ```
43    /// # use serde_json::json;
44    /// # use std::convert::TryInto;
45    /// # use ruma_serde::CanonicalJsonValue;
46    /// let v: CanonicalJsonValue = json!(12).try_into().unwrap();
47    /// ```
48    Integer(Int),
49
50    /// Represents a JSON string.
51    ///
52    /// ```
53    /// # use serde_json::json;
54    /// # use std::convert::TryInto;
55    /// # use ruma_serde::CanonicalJsonValue;
56    /// let v: CanonicalJsonValue = json!("a string").try_into().unwrap();
57    /// ```
58    String(String),
59
60    /// Represents a JSON array.
61    ///
62    /// ```
63    /// # use serde_json::json;
64    /// # use std::convert::TryInto;
65    /// # use ruma_serde::CanonicalJsonValue;
66    /// let v: CanonicalJsonValue = json!(["an", "array"]).try_into().unwrap();
67    /// ```
68    Array(Vec<CanonicalJsonValue>),
69
70    /// Represents a JSON object.
71    ///
72    /// The map is backed by a BTreeMap to guarantee the sorting of keys.
73    ///
74    /// ```
75    /// # use serde_json::json;
76    /// # use std::convert::TryInto;
77    /// # use ruma_serde::CanonicalJsonValue;
78    /// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
79    /// ```
80    Object(Object),
81}
82
83impl CanonicalJsonValue {
84    /// If the `CanonicalJsonValue` is a `Bool`, return the inner value.
85    pub fn as_bool(&self) -> Option<bool> {
86        match self {
87            Self::Bool(b) => Some(*b),
88            _ => None,
89        }
90    }
91
92    /// If the `CanonicalJsonValue` is an `Integer`, return the inner value.
93    pub fn as_integer(&self) -> Option<Int> {
94        match self {
95            Self::Integer(i) => Some(*i),
96            _ => None,
97        }
98    }
99
100    /// If the `CanonicalJsonValue` is a `String`, return a reference to the inner value.
101    pub fn as_str(&self) -> Option<&str> {
102        match self {
103            Self::String(s) => Some(s),
104            _ => None,
105        }
106    }
107
108    /// If the `CanonicalJsonValue` is an `Array`, return a reference to the inner value.
109    pub fn as_array(&self) -> Option<&[CanonicalJsonValue]> {
110        match self {
111            Self::Array(a) => Some(a),
112            _ => None,
113        }
114    }
115
116    /// If the `CanonicalJsonValue` is an `Object`, return a reference to the inner value.
117    pub fn as_object(&self) -> Option<&Object> {
118        match self {
119            Self::Object(o) => Some(o),
120            _ => None,
121        }
122    }
123
124    /// If the `CanonicalJsonValue` is an `Array`, return a mutable reference to the inner value.
125    pub fn as_array_mut(&mut self) -> Option<&mut Vec<CanonicalJsonValue>> {
126        match self {
127            Self::Array(a) => Some(a),
128            _ => None,
129        }
130    }
131
132    /// If the `CanonicalJsonValue` is an `Object`, return a mutable reference to the inner value.
133    pub fn as_object_mut(&mut self) -> Option<&mut Object> {
134        match self {
135            Self::Object(o) => Some(o),
136            _ => None,
137        }
138    }
139
140    /// Returns `true` if the `CanonicalJsonValue` is a `Bool`.
141    pub fn is_bool(&self) -> bool {
142        matches!(self, Self::Bool(_))
143    }
144
145    /// Returns `true` if the `CanonicalJsonValue` is an `Integer`.
146    pub fn is_integer(&self) -> bool {
147        matches!(self, Self::Integer(_))
148    }
149
150    /// Returns `true` if the `CanonicalJsonValue` is a `String`.
151    pub fn is_string(&self) -> bool {
152        matches!(self, Self::String(_))
153    }
154
155    /// Returns `true` if the `CanonicalJsonValue` is an `Array`.
156    pub fn is_array(&self) -> bool {
157        matches!(self, Self::Array(_))
158    }
159
160    /// Returns `true` if the `CanonicalJsonValue` is an `Object`.
161    pub fn is_object(&self) -> bool {
162        matches!(self, Self::Object(_))
163    }
164}
165
166impl Default for CanonicalJsonValue {
167    fn default() -> Self {
168        Self::Null
169    }
170}
171
172impl fmt::Debug for CanonicalJsonValue {
173    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
174        match *self {
175            Self::Null => formatter.debug_tuple("Null").finish(),
176            Self::Bool(v) => formatter.debug_tuple("Bool").field(&v).finish(),
177            Self::Integer(ref v) => fmt::Debug::fmt(v, formatter),
178            Self::String(ref v) => formatter.debug_tuple("String").field(v).finish(),
179            Self::Array(ref v) => {
180                formatter.write_str("Array(")?;
181                fmt::Debug::fmt(v, formatter)?;
182                formatter.write_str(")")
183            }
184            Self::Object(ref v) => {
185                formatter.write_str("Object(")?;
186                fmt::Debug::fmt(v, formatter)?;
187                formatter.write_str(")")
188            }
189        }
190    }
191}
192
193impl fmt::Display for CanonicalJsonValue {
194    /// Display this value as a string.
195    ///
196    /// This `Display` implementation is intentionally unaffected by any formatting parameters,
197    /// because adding extra whitespace or otherwise pretty-printing it would make it not the
198    /// canonical form anymore.
199    ///
200    /// If you want to pretty-print a `CanonicalJsonValue` for debugging purposes, use
201    /// one of `serde_json::{to_string_pretty, to_vec_pretty, to_writer_pretty}`.
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        write!(f, "{}", to_json_string(&self).map_err(|_| fmt::Error)?)
204    }
205}
206
207impl TryFrom<JsonValue> for CanonicalJsonValue {
208    type Error = Error;
209
210    fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
211        Ok(match val {
212            JsonValue::Bool(b) => Self::Bool(b),
213            JsonValue::Number(num) => Self::Integer(
214                Int::try_from(num.as_i64().ok_or(Error::IntConvert)?)
215                    .map_err(|_| Error::IntConvert)?,
216            ),
217            JsonValue::Array(vec) => {
218                Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?)
219            }
220            JsonValue::String(string) => Self::String(string),
221            JsonValue::Object(obj) => Self::Object(
222                obj.into_iter()
223                    .map(|(k, v)| Ok((k, v.try_into()?)))
224                    .collect::<Result<Object, _>>()?,
225            ),
226            JsonValue::Null => Self::Null,
227        })
228    }
229}
230
231impl From<CanonicalJsonValue> for JsonValue {
232    fn from(val: CanonicalJsonValue) -> Self {
233        match val {
234            CanonicalJsonValue::Bool(b) => Self::Bool(b),
235            CanonicalJsonValue::Integer(int) => Self::Number(i64::from(int).into()),
236            CanonicalJsonValue::String(string) => Self::String(string),
237            CanonicalJsonValue::Array(vec) => {
238                Self::Array(vec.into_iter().map(Into::into).collect())
239            }
240            CanonicalJsonValue::Object(obj) => {
241                Self::Object(obj.into_iter().map(|(k, v)| (k, v.into())).collect())
242            }
243            CanonicalJsonValue::Null => Self::Null,
244        }
245    }
246}
247
248macro_rules! variant_impls {
249    ($variant:ident($ty:ty)) => {
250        impl From<$ty> for CanonicalJsonValue {
251            fn from(val: $ty) -> Self {
252                Self::$variant(val)
253            }
254        }
255
256        impl PartialEq<$ty> for CanonicalJsonValue {
257            fn eq(&self, other: &$ty) -> bool {
258                match self {
259                    Self::$variant(val) => val == other,
260                    _ => false,
261                }
262            }
263        }
264
265        impl PartialEq<CanonicalJsonValue> for $ty {
266            fn eq(&self, other: &CanonicalJsonValue) -> bool {
267                match other {
268                    CanonicalJsonValue::$variant(val) => self == val,
269                    _ => false,
270                }
271            }
272        }
273    };
274}
275
276variant_impls!(Bool(bool));
277variant_impls!(Integer(Int));
278variant_impls!(String(String));
279variant_impls!(Array(Vec<CanonicalJsonValue>));
280variant_impls!(Object(Object));
281
282impl Serialize for CanonicalJsonValue {
283    #[inline]
284    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
285    where
286        S: Serializer,
287    {
288        match self {
289            Self::Null => serializer.serialize_unit(),
290            Self::Bool(b) => serializer.serialize_bool(*b),
291            Self::Integer(n) => n.serialize(serializer),
292            Self::String(s) => serializer.serialize_str(s),
293            Self::Array(v) => v.serialize(serializer),
294            Self::Object(m) => {
295                use serde::ser::SerializeMap;
296                let mut map = serializer.serialize_map(Some(m.len()))?;
297                for (k, v) in m {
298                    map.serialize_entry(k, v)?;
299                }
300                map.end()
301            }
302        }
303    }
304}
305
306impl<'de> Deserialize<'de> for CanonicalJsonValue {
307    #[inline]
308    fn deserialize<D>(deserializer: D) -> Result<CanonicalJsonValue, D::Error>
309    where
310        D: Deserializer<'de>,
311    {
312        let val = JsonValue::deserialize(deserializer)?;
313        val.try_into().map_err(serde::de::Error::custom)
314    }
315}
316
317#[cfg(test)]
318mod tests {
319    use std::convert::TryInto;
320
321    use serde_json::json;
322
323    use super::CanonicalJsonValue;
324
325    #[test]
326    fn to_string() {
327        const CANONICAL_STR: &str = r#"{"city":"London","street":"10 Downing Street"}"#;
328
329        let json: CanonicalJsonValue =
330            json!({ "city": "London", "street": "10 Downing Street" }).try_into().unwrap();
331
332        assert_eq!(format!("{}", json), CANONICAL_STR);
333        assert_eq!(format!("{:#}", json), CANONICAL_STR);
334    }
335}