common: Move CanonicalJson out of serde mod and behind a Cargo feature

This commit is contained in:
Jonas Platte 2022-06-17 18:50:36 +02:00 committed by Jonas Platte
parent e96b197d4d
commit 402b2764fb
10 changed files with 46 additions and 34 deletions

View File

@ -17,6 +17,8 @@ Breaking changes:
* Room name size limits were never enforced by servers * Room name size limits were never enforced by servers
([Spec change removing the size limit][spec]) ([Spec change removing the size limit][spec])
* Remove `RoomMessageFeedbackEvent` and associated types and variants according to MSC3582 * Remove `RoomMessageFeedbackEvent` and associated types and variants according to MSC3582
* Move `CanonicalJson`, `CanonicalJsonObject` and `CanonicalJsonError` out of
the `serde` module and behind the cargo feature flag `canonical-json`
[spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/3669 [spec]: https://github.com/matrix-org/matrix-spec-proposals/pull/3669

View File

@ -22,6 +22,7 @@ client = []
server = [] server = []
api = ["http", "thiserror"] api = ["http", "thiserror"]
canonical-json = []
compat = ["ruma-macros/compat", "ruma-identifiers-validation/compat"] compat = ["ruma-macros/compat", "ruma-identifiers-validation/compat"]
events = ["thiserror"] events = ["thiserror"]
js = ["js-sys", "getrandom?/js", "uuid?/js"] js = ["js-sys", "getrandom?/js", "uuid?/js"]

View File

@ -1,16 +1,19 @@
//! Canonical JSON types and related functions.
use std::fmt; use std::fmt;
use serde::Serialize; use serde::Serialize;
use serde_json::{Error as JsonError, Value as JsonValue}; use serde_json::{Error as JsonError, Value as JsonValue};
pub mod value; mod value;
use value::Object as CanonicalJsonObject; pub use self::value::{CanonicalJsonObject, CanonicalJsonValue};
/// The set of possible errors when serializing to canonical JSON. /// The set of possible errors when serializing to canonical JSON.
#[cfg(feature = "canonical-json")]
#[derive(Debug)] #[derive(Debug)]
#[allow(clippy::exhaustive_enums)] #[allow(clippy::exhaustive_enums)]
pub enum Error { pub enum CanonicalJsonError {
/// The numeric value failed conversion to js_int::Int. /// The numeric value failed conversion to js_int::Int.
IntConvert, IntConvert,
@ -18,27 +21,31 @@ pub enum Error {
SerDe(JsonError), SerDe(JsonError),
} }
impl fmt::Display for Error { impl fmt::Display for CanonicalJsonError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Error::IntConvert => f.write_str("number found is not a valid `js_int::Int`"), CanonicalJsonError::IntConvert => {
Error::SerDe(err) => write!(f, "serde Error: {err}"), f.write_str("number found is not a valid `js_int::Int`")
}
CanonicalJsonError::SerDe(err) => write!(f, "serde Error: {err}"),
} }
} }
} }
impl std::error::Error for Error {} impl std::error::Error for CanonicalJsonError {}
/// Fallible conversion from a `serde_json::Map` to a `CanonicalJsonObject`. /// Fallible conversion from a `serde_json::Map` to a `CanonicalJsonObject`.
pub fn try_from_json_map( pub fn try_from_json_map(
json: serde_json::Map<String, JsonValue>, json: serde_json::Map<String, JsonValue>,
) -> Result<CanonicalJsonObject, Error> { ) -> Result<CanonicalJsonObject, CanonicalJsonError> {
json.into_iter().map(|(k, v)| Ok((k, v.try_into()?))).collect() json.into_iter().map(|(k, v)| Ok((k, v.try_into()?))).collect()
} }
/// Fallible conversion from any value that impl's `Serialize` to a `CanonicalJsonValue`. /// 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> { pub fn to_canonical_value<T: Serialize>(
serde_json::to_value(value).map_err(Error::SerDe)?.try_into() value: T,
) -> Result<value::CanonicalJsonValue, CanonicalJsonError> {
serde_json::to_value(value).map_err(CanonicalJsonError::SerDe)?.try_into()
} }
#[cfg(test)] #[cfg(test)]

View File

@ -4,12 +4,14 @@ use js_int::Int;
use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize}; use serde::{de::Deserializer, ser::Serializer, Deserialize, Serialize};
use serde_json::{to_string as to_json_string, Value as JsonValue}; use serde_json::{to_string as to_json_string, Value as JsonValue};
use super::Error; use super::CanonicalJsonError;
/// The inner type of `CanonicalJsonValue::Object`. /// The inner type of `CanonicalJsonValue::Object`.
pub type Object = BTreeMap<String, CanonicalJsonValue>; #[cfg(feature = "canonical-json")]
pub type CanonicalJsonObject = BTreeMap<String, CanonicalJsonValue>;
/// Represents a canonical JSON value as per the Matrix specification. /// Represents a canonical JSON value as per the Matrix specification.
#[cfg(feature = "canonical-json")]
#[derive(Clone, Eq, PartialEq)] #[derive(Clone, Eq, PartialEq)]
#[allow(clippy::exhaustive_enums)] #[allow(clippy::exhaustive_enums)]
pub enum CanonicalJsonValue { pub enum CanonicalJsonValue {
@ -67,7 +69,7 @@ pub enum CanonicalJsonValue {
/// # use ruma_common::serde::CanonicalJsonValue; /// # use ruma_common::serde::CanonicalJsonValue;
/// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap(); /// let v: CanonicalJsonValue = json!({ "an": "object" }).try_into().unwrap();
/// ``` /// ```
Object(Object), Object(CanonicalJsonObject),
} }
impl CanonicalJsonValue { impl CanonicalJsonValue {
@ -104,7 +106,7 @@ impl CanonicalJsonValue {
} }
/// If the `CanonicalJsonValue` is an `Object`, return a reference to the inner value. /// If the `CanonicalJsonValue` is an `Object`, return a reference to the inner value.
pub fn as_object(&self) -> Option<&Object> { pub fn as_object(&self) -> Option<&CanonicalJsonObject> {
match self { match self {
Self::Object(o) => Some(o), Self::Object(o) => Some(o),
_ => None, _ => None,
@ -120,7 +122,7 @@ impl CanonicalJsonValue {
} }
/// If the `CanonicalJsonValue` is an `Object`, return a mutable reference to the inner value. /// If the `CanonicalJsonValue` is an `Object`, return a mutable reference to the inner value.
pub fn as_object_mut(&mut self) -> Option<&mut Object> { pub fn as_object_mut(&mut self) -> Option<&mut CanonicalJsonObject> {
match self { match self {
Self::Object(o) => Some(o), Self::Object(o) => Some(o),
_ => None, _ => None,
@ -195,14 +197,14 @@ impl fmt::Display for CanonicalJsonValue {
} }
impl TryFrom<JsonValue> for CanonicalJsonValue { impl TryFrom<JsonValue> for CanonicalJsonValue {
type Error = Error; type Error = CanonicalJsonError;
fn try_from(val: JsonValue) -> Result<Self, Self::Error> { fn try_from(val: JsonValue) -> Result<Self, Self::Error> {
Ok(match val { Ok(match val {
JsonValue::Bool(b) => Self::Bool(b), JsonValue::Bool(b) => Self::Bool(b),
JsonValue::Number(num) => Self::Integer( JsonValue::Number(num) => Self::Integer(
Int::try_from(num.as_i64().ok_or(Error::IntConvert)?) Int::try_from(num.as_i64().ok_or(CanonicalJsonError::IntConvert)?)
.map_err(|_| Error::IntConvert)?, .map_err(|_| CanonicalJsonError::IntConvert)?,
), ),
JsonValue::Array(vec) => { JsonValue::Array(vec) => {
Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?) Self::Array(vec.into_iter().map(TryInto::try_into).collect::<Result<Vec<_>, _>>()?)
@ -211,7 +213,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<Object, _>>()?, .collect::<Result<CanonicalJsonObject, _>>()?,
), ),
JsonValue::Null => Self::Null, JsonValue::Null => Self::Null,
}) })
@ -267,7 +269,7 @@ variant_impls!(Bool(bool));
variant_impls!(Integer(Int)); variant_impls!(Integer(Int));
variant_impls!(String(String)); variant_impls!(String(String));
variant_impls!(Array(Vec<CanonicalJsonValue>)); variant_impls!(Array(Vec<CanonicalJsonValue>));
variant_impls!(Object(Object)); variant_impls!(Object(CanonicalJsonObject));
impl Serialize for CanonicalJsonValue { impl Serialize for CanonicalJsonValue {
#[inline] #[inline]

View File

@ -25,6 +25,8 @@ extern crate self as ruma_common;
#[cfg(feature = "api")] #[cfg(feature = "api")]
pub mod api; pub mod api;
pub mod authentication; pub mod authentication;
#[cfg(feature = "canonical-json")]
pub mod canonical_json;
pub mod directory; pub mod directory;
pub mod encryption; pub mod encryption;
#[cfg(feature = "events")] #[cfg(feature = "events")]
@ -42,8 +44,12 @@ pub mod to_device;
use std::fmt; use std::fmt;
pub use identifiers::*; #[cfg(feature = "canonical-json")]
pub use time::{MilliSecondsSinceUnixEpoch, SecondsSinceUnixEpoch}; pub use self::canonical_json::{CanonicalJsonError, CanonicalJsonObject, CanonicalJsonValue};
pub use self::{
identifiers::*,
time::{MilliSecondsSinceUnixEpoch, SecondsSinceUnixEpoch},
};
// Wrapper around `Box<str>` that cannot be used in a meaningful way outside of // Wrapper around `Box<str>` that cannot be used in a meaningful way outside of
// this crate. Used for string enums because their `_Custom` variant can't be // this crate. Used for string enums because their `_Custom` variant can't be

View File

@ -11,7 +11,6 @@ use serde_json::{value::RawValue as RawJsonValue, Value as JsonValue};
pub mod base64; pub mod base64;
mod buf; mod buf;
pub mod can_be_empty; pub mod can_be_empty;
mod canonical_json;
mod cow; mod cow;
pub mod duration; pub mod duration;
mod empty; mod empty;
@ -26,11 +25,6 @@ pub use self::{
base64::{Base64, Base64DecodeError}, base64::{Base64, Base64DecodeError},
buf::{json_to_buf, slice_to_buf}, buf::{json_to_buf, slice_to_buf},
can_be_empty::{is_empty, CanBeEmpty}, can_be_empty::{is_empty, CanBeEmpty},
canonical_json::{
to_canonical_value, try_from_json_map,
value::{CanonicalJsonValue, Object as CanonicalJsonObject},
Error as CanonicalJsonError,
},
cow::deserialize_cow_str, cow::deserialize_cow_str,
empty::vec_as_map_of_empty, empty::vec_as_map_of_empty,
raw::Raw, raw::Raw,

View File

@ -25,7 +25,7 @@ ed25519-dalek = "1.0.1"
pkcs8 = { version = "0.9.0", features = ["alloc"] } pkcs8 = { version = "0.9.0", features = ["alloc"] }
# because dalek uses an older version of rand_core # because dalek uses an older version of rand_core
rand = { version = "0.7", features = ["getrandom"] } rand = { version = "0.7", features = ["getrandom"] }
ruma-common = { version = "0.9.2", path = "../ruma-common" } ruma-common = { version = "0.9.2", path = "../ruma-common", features = ["canonical-json"] }
serde_json = "1.0.60" serde_json = "1.0.60"
sha2 = "0.9.5" sha2 = "0.9.5"
subslice = { version = "0.2.3", optional = true } subslice = { version = "0.2.3", optional = true }

View File

@ -8,8 +8,8 @@ use std::{
use base64::{encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD}; use base64::{encode_config, STANDARD_NO_PAD, URL_SAFE_NO_PAD};
use ruma_common::{ use ruma_common::{
serde::{base64::Standard, Base64, CanonicalJsonObject, CanonicalJsonValue}, serde::{base64::Standard, Base64},
OwnedEventId, OwnedServerName, RoomVersionId, UserId, CanonicalJsonObject, CanonicalJsonValue, OwnedEventId, OwnedServerName, RoomVersionId, UserId,
}; };
use serde_json::{from_str as from_json_str, to_string as to_json_string}; use serde_json::{from_str as from_json_str, to_string as to_json_string};
use sha2::{digest::Digest, Sha256}; use sha2::{digest::Digest, Sha256};
@ -881,8 +881,7 @@ mod tests {
use assert_matches::assert_matches; use assert_matches::assert_matches;
use ruma_common::{ use ruma_common::{
serde::{Base64, CanonicalJsonValue}, serde::Base64, CanonicalJsonValue, RoomVersionId, ServerSigningKeyId, SigningKeyAlgorithm,
RoomVersionId, ServerSigningKeyId, SigningKeyAlgorithm,
}; };
use serde_json::json; use serde_json::json;

View File

@ -52,7 +52,7 @@ pub use functions::{
redact_in_place, reference_hash, sign_json, verify_event, verify_json, redact_in_place, reference_hash, sign_json, verify_event, verify_json,
}; };
pub use keys::{Ed25519KeyPair, KeyPair, PublicKeyMap, PublicKeySet}; pub use keys::{Ed25519KeyPair, KeyPair, PublicKeyMap, PublicKeySet};
pub use ruma_common::serde::{CanonicalJsonError, CanonicalJsonObject, CanonicalJsonValue}; pub use ruma_common::{CanonicalJsonError, CanonicalJsonObject, CanonicalJsonValue};
pub use signatures::Signature; pub use signatures::Signature;
pub use verification::Verified; pub use verification::Verified;

View File

@ -17,6 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[features] [features]
api = ["ruma-common/api"] api = ["ruma-common/api"]
canonical-json = ["ruma-common/canonical-json"]
client = ["ruma-client"] client = ["ruma-client"]
events = ["ruma-common/events"] events = ["ruma-common/events"]
signatures = ["ruma-signatures"] signatures = ["ruma-signatures"]