diff --git a/ruma-serde/src/canonical_json.rs b/ruma-serde/src/canonical_json.rs index fc45e214..7aba15f1 100644 --- a/ruma-serde/src/canonical_json.rs +++ b/ruma-serde/src/canonical_json.rs @@ -1 +1,98 @@ +use std::fmt; + +use serde::Serialize; +use serde_json::Error as JsonError; + pub mod value; + +/// Returns a canonical JSON string according to Matrix specification. +/// +/// This function should be preferred over `serde_json::to_string` since it checks the size of the +/// canonical string. Matrix canonical JSON enforces a size limit of less than 65,535 when sending +/// PDU's for the server-server protocol. +pub fn to_string(val: &T) -> Result { + let s = serde_json::to_string(val).map_err(Error::SerDe)?; + + if s.as_bytes().len() > 65_535 { + Err(Error::JsonSize) + } else { + Ok(s) + } +} + +/// The set of possible errors when serializing to canonical JSON. +#[derive(Debug)] +pub enum Error { + /// The numeric value failed conversion to js_int::Int. + IntConvert, + /// The `CanonicalJsonValue` being serialized was larger than 65,535 bytes. + JsonSize, + /// An error occurred while serializing/deserializing. + SerDe(JsonError), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match self { + Error::IntConvert => f.write_str("number found is not a valid `js_int::Int`"), + Error::JsonSize => f.write_str("JSON is larger than 65,535 byte max"), + Error::SerDe(err) => write!(f, "serde Error: {}", err), + } + } +} + +impl std::error::Error for Error {} + +#[cfg(test)] +mod test { + use std::convert::TryInto; + + use super::{to_string as to_canonical_json_string, value::CanonicalJsonValue}; + use serde_json::{from_str as from_json_str, json, to_string as to_json_string}; + + #[test] + fn serialize_canon() { + let json: CanonicalJsonValue = json!({ + "a": [1, 2, 3], + "other": { "stuff": "hello" }, + "string": "Thing" + }) + .try_into() + .unwrap(); + + let ser = to_canonical_json_string(&json).unwrap(); + let back = from_json_str::(&ser).unwrap(); + + assert_eq!(json, back); + } + + #[test] + fn check_canonical_sorts_keys() { + let json: CanonicalJsonValue = json!({ + "auth": { + "success": true, + "mxid": "@john.doe:example.com", + "profile": { + "display_name": "John Doe", + "three_pids": [ + { + "medium": "email", + "address": "john.doe@example.org" + }, + { + "medium": "msisdn", + "address": "123456789" + } + ] + } + } + }) + .try_into() + .unwrap(); + + assert_eq!( + to_json_string(&json).unwrap(), + 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}}"# + ) + } +} diff --git a/ruma-serde/src/canonical_json/value.rs b/ruma-serde/src/canonical_json/value.rs index 65e4d8ed..0522de0f 100644 --- a/ruma-serde/src/canonical_json/value.rs +++ b/ruma-serde/src/canonical_json/value.rs @@ -6,30 +6,9 @@ use std::{ use js_int::Int; use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; -use serde_json::{to_string as to_json_string, Error as JsonError, Value as JsonValue}; +use serde_json::{to_string as to_json_string, Value as JsonValue}; -/// The set of possible errors when serializing to canonical JSON. -#[derive(Debug)] -pub enum Error { - /// The numeric value failed conversion to js_int::Int. - IntConvert, - /// The `CanonicalJsonValue` being serialized was larger than 65,535 bytes. - JsonSize, - /// An error occurred while serializing/deserializing. - SerDe(JsonError), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match self { - Error::IntConvert => f.write_str("number found is not a valid `js_int::Int`"), - Error::JsonSize => f.write_str("JSON is larger than 65,535 byte max"), - Error::SerDe(err) => write!(f, "serde Error: {}", err), - } - } -} - -impl std::error::Error for Error {} +use super::Error; #[derive(Clone, Eq, PartialEq)] pub enum CanonicalJsonValue { @@ -96,23 +75,6 @@ pub enum CanonicalJsonValue { Object(BTreeMap), } -impl CanonicalJsonValue { - /// Returns a canonical JSON string according to Matrix specification. - /// - /// The method should be preferred over `serde_json::to_string` since it - /// checks the size of the canonical string. Matrix canonical JSON enforces - /// a size limit of less than 65,535 when sending PDU's for the server-server protocol. - pub fn to_canonical_string(&self) -> Result { - Ok(to_json_string(self).map_err(Error::SerDe).and_then(|s| { - if s.as_bytes().len() > 65_535 { - Err(Error::JsonSize) - } else { - Ok(s) - } - })?) - } -} - impl fmt::Debug for CanonicalJsonValue { fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -211,57 +173,3 @@ impl<'de> Deserialize<'de> for CanonicalJsonValue { Ok(val.try_into().map_err(serde::de::Error::custom)?) } } - -#[cfg(test)] -mod test { - use std::convert::TryInto; - - use super::CanonicalJsonValue; - use serde_json::{from_str as from_json_str, json, to_string as to_json_string}; - - #[test] - fn serialize_canon() { - let json: CanonicalJsonValue = json!({ - "a": [1, 2, 3], - "other": { "stuff": "hello" }, - "string": "Thing" - }) - .try_into() - .unwrap(); - - let ser = json.to_canonical_string().unwrap(); - let back = from_json_str::(&ser).unwrap(); - - assert_eq!(json, back); - } - - #[test] - fn check_canonical_sorts_keys() { - let json: CanonicalJsonValue = json!({ - "auth": { - "success": true, - "mxid": "@john.doe:example.com", - "profile": { - "display_name": "John Doe", - "three_pids": [ - { - "medium": "email", - "address": "john.doe@example.org" - }, - { - "medium": "msisdn", - "address": "123456789" - } - ] - } - } - }) - .try_into() - .unwrap(); - - assert_eq!( - to_json_string(&json).unwrap(), - 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}}"# - ) - } -} diff --git a/ruma-serde/src/lib.rs b/ruma-serde/src/lib.rs index c0d06baf..0979c13e 100644 --- a/ruma-serde/src/lib.rs +++ b/ruma-serde/src/lib.rs @@ -13,7 +13,9 @@ pub mod time; pub mod urlencoded; pub use can_be_empty::{is_empty, CanBeEmpty}; -pub use canonical_json::value::{CanonicalJsonValue, Error as CanonicalError}; +pub use canonical_json::{ + to_string as to_canonical_json_string, value::CanonicalJsonValue, Error as CanonicalJsonError, +}; pub use empty::vec_as_map_of_empty; /// Check whether a value is equal to its default value.