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_json::Error as JsonError;
use serde_json::{Error as JsonError, Map as JsonObject, Value as JsonValue};
pub mod value;
use value::Object as CanonicalJsonObject;
/// 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
@ -45,12 +47,29 @@ impl fmt::Display 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)]
mod test {
use std::convert::TryInto;
use std::{collections::BTreeMap, 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};
use super::{
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]
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}}"#
)
}
#[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;
/// The inner type of `CanonicalJsonValue::Object`.
pub type Object = BTreeMap<String, CanonicalJsonValue>;
#[derive(Clone, Eq, PartialEq)]
pub enum CanonicalJsonValue {
/// Represents a JSON null value.
@ -72,7 +75,7 @@ pub enum CanonicalJsonValue {
/// # use ruma_serde::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
/// ```
Object(BTreeMap<String, CanonicalJsonValue>),
Object(Object),
}
impl Default for CanonicalJsonValue {
@ -133,7 +136,7 @@ impl TryFrom<JsonValue> for CanonicalJsonValue {
JsonValue::Object(obj) => Self::Object(
obj.into_iter()
.map(|(k, v)| Ok((k, v.try_into()?)))
.collect::<Result<BTreeMap<_, _>, _>>()?,
.collect::<Result<Object, _>>()?,
),
JsonValue::Null => Self::Null,
})

View File

@ -15,7 +15,9 @@ pub mod urlencoded;
pub use can_be_empty::{is_empty, CanBeEmpty};
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 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 ring::digest::{digest, SHA256};
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 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.
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`.
///
/// 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::{
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 signatures::Signature;