Add helper methods for CanonicalJsonObject construction

This commit is contained in:
Devin Ragotzy 2020-10-29 11:31:23 -07:00 committed by GitHub
parent c04a9e71c5
commit bc43e94d7e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 77 additions and 13 deletions

View File

@ -1,10 +1,12 @@
use std::fmt; use std::{convert::TryInto, fmt};
use serde::Serialize; use serde::Serialize;
use serde_json::Error as JsonError; use serde_json::{Error as JsonError, Map as JsonObject, Value as JsonValue};
pub mod value; pub mod value;
use value::Object as CanonicalJsonObject;
/// Returns a canonical JSON string according to Matrix specification. /// 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 /// This function should be preferred over `serde_json::to_string` since it checks the size of the
@ -45,12 +47,29 @@ impl fmt::Display for Error {
impl std::error::Error for Error {} impl std::error::Error for Error {}
/// Fallible conversion from a `serde_json::Map` to a `CanonicalJsonObject`.
pub fn try_from_json_map(
json: JsonObject<String, JsonValue>,
) -> Result<CanonicalJsonObject, Error> {
json.into_iter().map(|(k, v)| Ok((k, v.try_into()?))).collect()
}
/// Fallible conversion from any value that impl's `Serialize` to a `CanonicalJsonValue`.
pub fn to_canonical_value<T: Serialize>(value: T) -> Result<value::CanonicalJsonValue, Error> {
serde_json::to_value(value).map_err(Error::SerDe)?.try_into()
}
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::convert::TryInto; use std::{collections::BTreeMap, convert::TryInto};
use super::{to_string as to_canonical_json_string, value::CanonicalJsonValue}; use super::{
use serde_json::{from_str as from_json_str, json, to_string as to_json_string}; to_canonical_value, to_string as to_canonical_json_string, try_from_json_map,
value::CanonicalJsonValue,
};
use js_int::int;
use serde_json::{from_str as from_json_str, json, to_string as to_json_string, Map};
#[test] #[test]
fn serialize_canon() { fn serialize_canon() {
@ -97,4 +116,47 @@ mod test {
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}}"# 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}}"#
) )
} }
#[test]
fn serialize_map_to_canonical() {
let mut expected = BTreeMap::new();
expected.insert("foo".into(), CanonicalJsonValue::String("string".into()));
expected.insert(
"bar".into(),
CanonicalJsonValue::Array(vec![
CanonicalJsonValue::Integer(int!(0)),
CanonicalJsonValue::Integer(int!(1)),
CanonicalJsonValue::Integer(int!(2)),
]),
);
let mut map = Map::new();
map.insert("foo".into(), json!("string"));
map.insert("bar".into(), json!(vec![0, 1, 2,]));
assert_eq!(try_from_json_map(map).unwrap(), expected);
}
#[test]
fn to_canonical() {
#[derive(Debug, serde::Serialize)]
struct Thing {
foo: String,
bar: Vec<u8>,
}
let t = Thing { foo: "string".into(), bar: vec![0, 1, 2] };
let mut expected = BTreeMap::new();
expected.insert("foo".into(), CanonicalJsonValue::String("string".into()));
expected.insert(
"bar".into(),
CanonicalJsonValue::Array(vec![
CanonicalJsonValue::Integer(int!(0)),
CanonicalJsonValue::Integer(int!(1)),
CanonicalJsonValue::Integer(int!(2)),
]),
);
assert_eq!(to_canonical_value(t).unwrap(), CanonicalJsonValue::Object(expected));
}
} }

View File

@ -10,6 +10,9 @@ use serde_json::{to_string as to_json_string, Value as JsonValue};
use super::Error; use super::Error;
/// The inner type of `CanonicalJsonValue::Object`.
pub type Object = BTreeMap<String, CanonicalJsonValue>;
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
pub enum CanonicalJsonValue { pub enum CanonicalJsonValue {
/// Represents a JSON null value. /// Represents a JSON null value.
@ -72,7 +75,7 @@ pub enum CanonicalJsonValue {
/// # use ruma_serde::CanonicalJsonValue; /// # use ruma_serde::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap(); /// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
/// ``` /// ```
Object(BTreeMap<String, CanonicalJsonValue>), Object(Object),
} }
impl Default for CanonicalJsonValue { impl Default for CanonicalJsonValue {
@ -133,7 +136,7 @@ impl TryFrom<JsonValue> for CanonicalJsonValue {
JsonValue::Object(obj) => Self::Object( JsonValue::Object(obj) => Self::Object(
obj.into_iter() obj.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?))) .map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<Result<BTreeMap<_, _>, _>>()?, .collect::<Result<Object, _>>()?,
), ),
JsonValue::Null => Self::Null, JsonValue::Null => Self::Null,
}) })

View File

@ -15,7 +15,9 @@ pub mod urlencoded;
pub use can_be_empty::{is_empty, CanBeEmpty}; pub use can_be_empty::{is_empty, CanBeEmpty};
pub use canonical_json::{ pub use canonical_json::{
to_string as to_canonical_json_string, value::CanonicalJsonValue, Error as CanonicalJsonError, to_canonical_value, to_string as to_canonical_json_string, try_from_json_map,
value::{CanonicalJsonValue, Object as CanonicalJsonObject},
Error as CanonicalJsonError,
}; };
pub use cow::deserialize_cow_str; pub use cow::deserialize_cow_str;
pub use empty::vec_as_map_of_empty; pub use empty::vec_as_map_of_empty;

View File

@ -5,7 +5,7 @@ use std::{collections::BTreeMap, mem};
use base64::{decode_config, encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD}; use base64::{decode_config, encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
use ring::digest::{digest, SHA256}; use ring::digest::{digest, SHA256};
use ruma_identifiers::RoomVersionId; use ruma_identifiers::RoomVersionId;
use ruma_serde::{to_canonical_json_string, CanonicalJsonValue}; use ruma_serde::{to_canonical_json_string, CanonicalJsonObject, CanonicalJsonValue};
use serde_json::from_str as from_json_str; use serde_json::from_str as from_json_str;
use crate::{ use crate::{
@ -73,9 +73,6 @@ static CONTENT_HASH_FIELDS_TO_REMOVE: &[&str] = &["hashes", "signatures", "unsig
/// The fields to remove from a JSON object when creating a reference hash of an event. /// The fields to remove from a JSON object when creating a reference hash of an event.
static REFERENCE_HASH_FIELDS_TO_REMOVE: &[&str] = &["age_ts", "signatures", "unsigned"]; static REFERENCE_HASH_FIELDS_TO_REMOVE: &[&str] = &["age_ts", "signatures", "unsigned"];
/// The inner type of `CanonicalJsonValue::Object`.
pub type CanonicalJsonObject = BTreeMap<String, CanonicalJsonValue>;
/// Signs an arbitrary JSON object and adds the signature to an object under the key `signatures`. /// Signs an arbitrary JSON object and adds the signature to an object under the key `signatures`.
/// ///
/// If `signatures` is already present, the new signature will be appended to the existing ones. /// If `signatures` is already present, the new signature will be appended to the existing ones.

View File

@ -51,7 +51,7 @@ use std::{
pub use functions::{ pub use functions::{
canonical_json, content_hash, hash_and_sign_event, redact, reference_hash, sign_json, canonical_json, content_hash, hash_and_sign_event, redact, reference_hash, sign_json,
verify_event, verify_json, CanonicalJsonObject, verify_event, verify_json,
}; };
pub use keys::{Ed25519KeyPair, KeyPair, PublicKeyMap, PublicKeySet}; pub use keys::{Ed25519KeyPair, KeyPair, PublicKeyMap, PublicKeySet};
pub use signatures::Signature; pub use signatures::Signature;