ruma_serde/
canonical_json.rs

1use std::{convert::TryInto, fmt};
2
3use serde::Serialize;
4use serde_json::{Error as JsonError, Value as JsonValue};
5
6pub mod value;
7
8use value::Object as CanonicalJsonObject;
9
10/// The set of possible errors when serializing to canonical JSON.
11#[derive(Debug)]
12#[allow(clippy::exhaustive_enums)]
13pub enum Error {
14    /// The numeric value failed conversion to js_int::Int.
15    IntConvert,
16
17    /// An error occurred while serializing/deserializing.
18    SerDe(JsonError),
19}
20
21impl fmt::Display for Error {
22    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23        match self {
24            Error::IntConvert => f.write_str("number found is not a valid `js_int::Int`"),
25            Error::SerDe(err) => write!(f, "serde Error: {}", err),
26        }
27    }
28}
29
30impl std::error::Error for Error {}
31
32/// Fallible conversion from a `serde_json::Map` to a `CanonicalJsonObject`.
33pub fn try_from_json_map(
34    json: serde_json::Map<String, JsonValue>,
35) -> Result<CanonicalJsonObject, Error> {
36    json.into_iter().map(|(k, v)| Ok((k, v.try_into()?))).collect()
37}
38
39/// Fallible conversion from any value that impl's `Serialize` to a `CanonicalJsonValue`.
40pub fn to_canonical_value<T: Serialize>(value: T) -> Result<value::CanonicalJsonValue, Error> {
41    serde_json::to_value(value).map_err(Error::SerDe)?.try_into()
42}
43
44#[cfg(test)]
45mod tests {
46    use std::{collections::BTreeMap, convert::TryInto};
47
48    use js_int::int;
49    use serde_json::{from_str as from_json_str, json, to_string as to_json_string};
50
51    use super::{to_canonical_value, try_from_json_map, value::CanonicalJsonValue};
52
53    #[test]
54    fn serialize_canon() {
55        let json: CanonicalJsonValue = json!({
56            "a": [1, 2, 3],
57            "other": { "stuff": "hello" },
58            "string": "Thing"
59        })
60        .try_into()
61        .unwrap();
62
63        let ser = to_json_string(&json).unwrap();
64        let back = from_json_str::<CanonicalJsonValue>(&ser).unwrap();
65
66        assert_eq!(json, back);
67    }
68
69    #[test]
70    fn check_canonical_sorts_keys() {
71        let json: CanonicalJsonValue = json!({
72            "auth": {
73                "success": true,
74                "mxid": "@john.doe:example.com",
75                "profile": {
76                    "display_name": "John Doe",
77                    "three_pids": [
78                        {
79                            "medium": "email",
80                            "address": "john.doe@example.org"
81                        },
82                        {
83                            "medium": "msisdn",
84                            "address": "123456789"
85                        }
86                    ]
87                }
88            }
89        })
90        .try_into()
91        .unwrap();
92
93        assert_eq!(
94            to_json_string(&json).unwrap(),
95            r#"{"auth":{"mxid":"@john.doe:example.com","profile":{"display_name":"John Doe","three_pids":[{"address":"john.doe@example.org","medium":"email"},{"address":"123456789","medium":"msisdn"}]},"success":true}}"#
96        )
97    }
98
99    #[test]
100    fn serialize_map_to_canonical() {
101        let mut expected = BTreeMap::new();
102        expected.insert("foo".into(), CanonicalJsonValue::String("string".into()));
103        expected.insert(
104            "bar".into(),
105            CanonicalJsonValue::Array(vec![
106                CanonicalJsonValue::Integer(int!(0)),
107                CanonicalJsonValue::Integer(int!(1)),
108                CanonicalJsonValue::Integer(int!(2)),
109            ]),
110        );
111
112        let mut map = serde_json::Map::new();
113        map.insert("foo".into(), json!("string"));
114        map.insert("bar".into(), json!(vec![0, 1, 2,]));
115
116        assert_eq!(try_from_json_map(map).unwrap(), expected);
117    }
118
119    #[test]
120    fn to_canonical() {
121        #[derive(Debug, serde::Serialize)]
122        struct Thing {
123            foo: String,
124            bar: Vec<u8>,
125        }
126        let t = Thing { foo: "string".into(), bar: vec![0, 1, 2] };
127
128        let mut expected = BTreeMap::new();
129        expected.insert("foo".into(), CanonicalJsonValue::String("string".into()));
130        expected.insert(
131            "bar".into(),
132            CanonicalJsonValue::Array(vec![
133                CanonicalJsonValue::Integer(int!(0)),
134                CanonicalJsonValue::Integer(int!(1)),
135                CanonicalJsonValue::Integer(int!(2)),
136            ]),
137        );
138
139        assert_eq!(to_canonical_value(t).unwrap(), CanonicalJsonValue::Object(expected));
140    }
141}