ruma_common/canonical_json/
value.rs

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