From 3ba15dfde02e6b2b97007580e6d6d3b7bfd55197 Mon Sep 17 00:00:00 2001 From: slinkydeveloper <francescoguard@gmail.com> Date: Mon, 30 Nov 2020 13:29:20 +0100 Subject: [PATCH 1/3] Removed serde_value Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> --- Cargo.toml | 3 +- src/event/format.rs | 133 +++++++++++++--------------------------- src/event/v03/format.rs | 27 ++++---- src/event/v10/format.rs | 27 ++++---- tests/serde_yaml.rs | 33 ++++++++++ 5 files changed, 104 insertions(+), 119 deletions(-) create mode 100644 tests/serde_yaml.rs diff --git a/Cargo.toml b/Cargo.toml index 01cbe5a9..6ea8053a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,12 +19,12 @@ name = "cloudevents" [dependencies] serde = { version = "^1.0", features = ["derive"] } serde_json = "^1.0" -serde-value = "^0.6" chrono = { version = "^0.4", features = ["serde"] } delegate-attr = "^0.2" base64 = "^0.12" url = { version = "^2.1", features = ["serde"] } snafu = "^0.6" +bitflags = "^1.2" [target."cfg(not(target_arch = \"wasm32\"))".dependencies] hostname = "^0.3" @@ -38,6 +38,7 @@ uuid = { version = "^0.8", features = ["v4", "wasm-bindgen"] } rstest = "0.6" claim = "0.3.1" version-sync = "^0.9" +serde_yaml = "0.8" [workspace] members = [ diff --git a/src/event/format.rs b/src/event/format.rs index 6c2d3a04..36f1b26c 100644 --- a/src/event/format.rs +++ b/src/event/format.rs @@ -5,67 +5,58 @@ use super::{ use crate::event::{AttributesReader, ExtensionValue}; use serde::de::{Error, IntoDeserializer, Unexpected}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde_value::Value; -use std::collections::{BTreeMap, HashMap}; +use serde_json::{Value, Map}; +use std::collections::HashMap; -macro_rules! parse_optional_field { - ($map:ident, $name:literal, $value_variant:ident, $error:ty) => { +macro_rules! parse_field { + ($value:expr, $target_type:ty, $error:ty) => { + <$target_type>::deserialize($value.into_deserializer()) + .map_err(<$error>::custom) + }; + + ($value:expr, $target_type:ty, $error:ty, $mapper:expr) => { + <$target_type>::deserialize($value.into_deserializer()) + .map_err(<$error>::custom) + .and_then(|v| $mapper(v).map_err(<$error>::custom)) + }; +} + +macro_rules! extract_optional_field { + ($map:ident, $name:literal, $target_type:ty, $error:ty) => { $map.remove($name) - .map(|val| match val { - Value::$value_variant(v) => Ok(v), - other => Err(<$error>::invalid_type( - crate::event::format::value_to_unexpected(&other), - &stringify!($value_variant), - )), - }) + .map(|v| parse_field!(v, $target_type, $error)) .transpose() }; - ($map:ident, $name:literal, $value_variant:ident, $error:ty, $mapper:expr) => { + ($map:ident, $name:literal, $target_type:ty, $error:ty, $mapper:expr) => { $map.remove($name) - .map(|val| match val { - Value::$value_variant(v) => $mapper(&v).map_err(|e| { - <$error>::invalid_value( - crate::event::format::value_to_unexpected(&Value::$value_variant(v)), - &e.to_string().as_str(), - ) - }), - other => Err(<$error>::invalid_type( - crate::event::format::value_to_unexpected(&other), - &stringify!($value_variant), - )), - }) + .map(|v| parse_field!(v, $target_type, $error, $mapper)) .transpose() }; } -macro_rules! parse_field { - ($map:ident, $name:literal, $value_variant:ident, $error:ty) => { - parse_optional_field!($map, $name, $value_variant, $error)? +macro_rules! extract_field { + ($map:ident, $name:literal, $target_type:ty, $error:ty) => { + extract_optional_field!($map, $name, $target_type, $error)? .ok_or_else(|| <$error>::missing_field($name)) }; - ($map:ident, $name:literal, $value_variant:ident, $error:ty, $mapper:expr) => { - parse_optional_field!($map, $name, $value_variant, $error, $mapper)? + ($map:ident, $name:literal, $target_type:ty, $error:ty, $mapper:expr) => { + extract_optional_field!($map, $name, $target_type, $error, $mapper)? .ok_or_else(|| <$error>::missing_field($name)) }; } macro_rules! parse_data_json { ($in:ident, $error:ty) => { - serde_json::Value::deserialize($in.into_deserializer()).map_err(<$error>::custom) + serde_json::Value::deserialize($in.into_deserializer()) + .map_err(<$error>::custom) }; } macro_rules! parse_data_string { ($in:ident, $error:ty) => { - match $in { - Value::String(s) => Ok(s), - other => Err(E::invalid_type( - crate::event::format::value_to_unexpected(&other), - &"a string", - )), - } + parse_field!($in, String, $error) }; } @@ -78,41 +69,37 @@ macro_rules! parse_json_data_base64 { macro_rules! parse_data_base64 { ($in:ident, $error:ty) => { - match $in { - Value::String(s) => base64::decode(&s).map_err(|e| { + parse_field!($in, String, $error) + .and_then(|s| base64::decode(&s).map_err(|e| { <$error>::invalid_value(serde::de::Unexpected::Str(&s), &e.to_string().as_str()) - }), - other => Err(E::invalid_type( - crate::event::format::value_to_unexpected(&other), - &"a string", - )), - } + })) }; } pub(crate) trait EventFormatDeserializer { fn deserialize_attributes<E: serde::de::Error>( - map: &mut BTreeMap<String, Value>, + map: &mut Map<String, Value>, ) -> Result<Attributes, E>; fn deserialize_data<E: serde::de::Error>( content_type: &str, - map: &mut BTreeMap<String, Value>, + map: &mut Map<String, Value>, ) -> Result<Option<Data>, E>; fn deserialize_event<E: serde::de::Error>( - mut map: BTreeMap<String, Value>, + mut map: Map<String, Value>, ) -> Result<Event, E> { let attributes = Self::deserialize_attributes(&mut map)?; let data = Self::deserialize_data( - attributes.datacontenttype().unwrap_or("application/json"), + attributes + .datacontenttype() + .unwrap_or("application/json"), &mut map, )?; let extensions = map .into_iter() - .map(|(k, v)| Ok((k, ExtensionValue::deserialize(v.into_deserializer())?))) - .collect::<Result<HashMap<String, ExtensionValue>, serde_value::DeserializerError>>() - .map_err(E::custom)?; + .map(|(k, v)| Ok((k, ExtensionValue::deserialize(v.into_deserializer()).map_err(E::custom)?))) + .collect::<Result<HashMap<String, ExtensionValue>, E>>()?; Ok(Event { attributes, @@ -136,20 +123,11 @@ impl<'de> Deserialize<'de> for Event { where D: Deserializer<'de>, { - let map = match Value::deserialize(deserializer)? { - Value::Map(m) => Ok(m), - v => Err(Error::invalid_type(value_to_unexpected(&v), &"a map")), - }?; - - let mut map: BTreeMap<String, Value> = map - .into_iter() - .map(|(k, v)| match k { - Value::String(s) => Ok((s, v)), - k => Err(Error::invalid_type(value_to_unexpected(&k), &"a string")), - }) - .collect::<Result<BTreeMap<String, Value>, <D as Deserializer<'de>>::Error>>()?; + let root_value = Value::deserialize(deserializer)?; + let mut map: Map<String, Value> = Map::deserialize(root_value.into_deserializer()) + .map_err(D::Error::custom)?; - match parse_field!(map, "specversion", String, <D as Deserializer<'de>>::Error)?.as_str() { + match extract_field!(map, "specversion", String, <D as Deserializer<'de>>::Error)?.as_str() { "0.3" => EventFormatDeserializerV03::deserialize_event(map), "1.0" => EventFormatDeserializerV10::deserialize_event(map), s => Err(D::Error::unknown_variant( @@ -175,28 +153,3 @@ impl Serialize for Event { } } } - -// This should be provided by the Value package itself -pub(crate) fn value_to_unexpected(v: &Value) -> Unexpected { - match v { - Value::Bool(b) => serde::de::Unexpected::Bool(*b), - Value::U8(n) => serde::de::Unexpected::Unsigned(*n as u64), - Value::U16(n) => serde::de::Unexpected::Unsigned(*n as u64), - Value::U32(n) => serde::de::Unexpected::Unsigned(*n as u64), - Value::U64(n) => serde::de::Unexpected::Unsigned(*n), - Value::I8(n) => serde::de::Unexpected::Signed(*n as i64), - Value::I16(n) => serde::de::Unexpected::Signed(*n as i64), - Value::I32(n) => serde::de::Unexpected::Signed(*n as i64), - Value::I64(n) => serde::de::Unexpected::Signed(*n), - Value::F32(n) => serde::de::Unexpected::Float(*n as f64), - Value::F64(n) => serde::de::Unexpected::Float(*n), - Value::Char(c) => serde::de::Unexpected::Char(*c), - Value::String(s) => serde::de::Unexpected::Str(s), - Value::Unit => serde::de::Unexpected::Unit, - Value::Option(_) => serde::de::Unexpected::Option, - Value::Newtype(_) => serde::de::Unexpected::NewtypeStruct, - Value::Seq(_) => serde::de::Unexpected::Seq, - Value::Map(_) => serde::de::Unexpected::Map, - Value::Bytes(b) => serde::de::Unexpected::Bytes(b), - } -} diff --git a/src/event/v03/format.rs b/src/event/v03/format.rs index 3009dcbf..339688a2 100644 --- a/src/event/v03/format.rs +++ b/src/event/v03/format.rs @@ -5,33 +5,32 @@ use chrono::{DateTime, Utc}; use serde::de::IntoDeserializer; use serde::ser::SerializeMap; use serde::{Deserialize, Serializer}; -use serde_value::Value; -use std::collections::{BTreeMap, HashMap}; +use serde_json::{Value, Map}; +use std::collections::HashMap; use url::Url; pub(crate) struct EventFormatDeserializer {} impl crate::event::format::EventFormatDeserializer for EventFormatDeserializer { fn deserialize_attributes<E: serde::de::Error>( - map: &mut BTreeMap<String, Value>, + map: &mut Map<String, Value>, ) -> Result<crate::event::Attributes, E> { Ok(crate::event::Attributes::V03(Attributes { - id: parse_field!(map, "id", String, E)?, - ty: parse_field!(map, "type", String, E)?, - source: parse_field!(map, "source", String, E, Url::parse)?, - datacontenttype: parse_optional_field!(map, "datacontenttype", String, E)?, - schemaurl: parse_optional_field!(map, "schemaurl", String, E, Url::parse)?, - subject: parse_optional_field!(map, "subject", String, E)?, - time: parse_optional_field!(map, "time", String, E, |s| DateTime::parse_from_rfc3339( - s - ) - .map(DateTime::<Utc>::from))?, + id: extract_field!(map, "id", String, E)?, + ty: extract_field!(map, "type", String, E)?, + source: extract_field!(map, "source", String, E, |s: String| Url::parse(&s))?, + datacontenttype: extract_optional_field!(map, "datacontenttype", String, E)?, + schemaurl: extract_optional_field!(map, "schemaurl", String, E, |s: String| Url::parse(&s))?, + subject: extract_optional_field!(map, "subject", String, E)?, + time: extract_optional_field!(map, "time", String, E, + |s: String| DateTime::parse_from_rfc3339(&s).map(DateTime::<Utc>::from) + )?, })) } fn deserialize_data<E: serde::de::Error>( content_type: &str, - map: &mut BTreeMap<String, Value>, + map: &mut Map<String, Value>, ) -> Result<Option<Data>, E> { let data = map.remove("data"); let is_base64 = map diff --git a/src/event/v10/format.rs b/src/event/v10/format.rs index ff54b322..47a1a5df 100644 --- a/src/event/v10/format.rs +++ b/src/event/v10/format.rs @@ -5,33 +5,32 @@ use chrono::{DateTime, Utc}; use serde::de::IntoDeserializer; use serde::ser::SerializeMap; use serde::{Deserialize, Serializer}; -use serde_value::Value; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use url::Url; +use serde_json::{Map, Value}; pub(crate) struct EventFormatDeserializer {} impl crate::event::format::EventFormatDeserializer for EventFormatDeserializer { fn deserialize_attributes<E: serde::de::Error>( - map: &mut BTreeMap<String, Value>, + map: &mut Map<String, Value>, ) -> Result<crate::event::Attributes, E> { Ok(crate::event::Attributes::V10(Attributes { - id: parse_field!(map, "id", String, E)?, - ty: parse_field!(map, "type", String, E)?, - source: parse_field!(map, "source", String, E, Url::parse)?, - datacontenttype: parse_optional_field!(map, "datacontenttype", String, E)?, - dataschema: parse_optional_field!(map, "dataschema", String, E, Url::parse)?, - subject: parse_optional_field!(map, "subject", String, E)?, - time: parse_optional_field!(map, "time", String, E, |s| DateTime::parse_from_rfc3339( - s - ) - .map(DateTime::<Utc>::from))?, + id: extract_field!(map, "id", String, E)?, + ty: extract_field!(map, "type", String, E)?, + source: extract_field!(map, "source", String, E, |s: String| Url::parse(&s))?, + datacontenttype: extract_optional_field!(map, "datacontenttype", String, E)?, + dataschema: extract_optional_field!(map, "dataschema", String, E, |s: String| Url::parse(&s))?, + subject: extract_optional_field!(map, "subject", String, E)?, + time: extract_optional_field!(map, "time", String, E, + |s: String| DateTime::parse_from_rfc3339(&s).map(DateTime::<Utc>::from) + )?, })) } fn deserialize_data<E: serde::de::Error>( content_type: &str, - map: &mut BTreeMap<String, Value>, + map: &mut Map<String, Value>, ) -> Result<Option<Data>, E> { let data = map.remove("data"); let data_base64 = map.remove("data_base64"); diff --git a/tests/serde_yaml.rs b/tests/serde_yaml.rs new file mode 100644 index 00000000..e6593165 --- /dev/null +++ b/tests/serde_yaml.rs @@ -0,0 +1,33 @@ +use claim::*; +use cloudevents::{Event, EventBuilder, EventBuilderV10}; +use serde_yaml; + +mod test_data; +use test_data::*; + +/// This test checks if the usage of serde_json::Value makes the Deserialize implementation incompatible with +/// other Deserializers +#[test] +fn deserialize_should_succeed() { + let input = r#" + id: aaa + type: bbb + source: http://localhost + datacontenttype: application/json + data: true + specversion: "1.0" + "#; + + let expected = EventBuilderV10::new() + .id("aaa") + .ty("bbb") + .source("http://localhost") + .data("application/json", serde_json::Value::Bool(true)) + .build() + .unwrap(); + + let deserialize_result: Result<Event, serde_yaml::Error> = serde_yaml::from_str(input); + assert_ok!(&deserialize_result); + let deserialized = deserialize_result.unwrap(); + assert_eq!(deserialized, expected) +} From 49d305e12755ae4744f8b9a3b6f5399fb8e3cc97 Mon Sep 17 00:00:00 2001 From: slinkydeveloper <francescoguard@gmail.com> Date: Mon, 30 Nov 2020 13:30:30 +0100 Subject: [PATCH 2/3] clippy + fix Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> --- src/event/format.rs | 2 +- tests/serde_yaml.rs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/event/format.rs b/src/event/format.rs index 36f1b26c..093e0f17 100644 --- a/src/event/format.rs +++ b/src/event/format.rs @@ -3,7 +3,7 @@ use super::{ EventFormatSerializerV03, EventFormatSerializerV10, }; use crate::event::{AttributesReader, ExtensionValue}; -use serde::de::{Error, IntoDeserializer, Unexpected}; +use serde::de::{Error, IntoDeserializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{Value, Map}; use std::collections::HashMap; diff --git a/tests/serde_yaml.rs b/tests/serde_yaml.rs index e6593165..ea917142 100644 --- a/tests/serde_yaml.rs +++ b/tests/serde_yaml.rs @@ -3,7 +3,6 @@ use cloudevents::{Event, EventBuilder, EventBuilderV10}; use serde_yaml; mod test_data; -use test_data::*; /// This test checks if the usage of serde_json::Value makes the Deserialize implementation incompatible with /// other Deserializers From 260be72881ff10dd353c298027e90995b31f8611 Mon Sep 17 00:00:00 2001 From: slinkydeveloper <francescoguard@gmail.com> Date: Mon, 30 Nov 2020 13:34:05 +0100 Subject: [PATCH 3/3] cargo fmt Signed-off-by: Francesco Guardiani <francescoguard@gmail.com> --- src/event/format.rs | 37 +++++++++++++++++++------------------ src/event/v03/format.rs | 12 +++++++----- src/event/v10/format.rs | 12 +++++++----- 3 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/event/format.rs b/src/event/format.rs index 093e0f17..5eed6f82 100644 --- a/src/event/format.rs +++ b/src/event/format.rs @@ -5,13 +5,12 @@ use super::{ use crate::event::{AttributesReader, ExtensionValue}; use serde::de::{Error, IntoDeserializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use serde_json::{Value, Map}; +use serde_json::{Map, Value}; use std::collections::HashMap; macro_rules! parse_field { ($value:expr, $target_type:ty, $error:ty) => { - <$target_type>::deserialize($value.into_deserializer()) - .map_err(<$error>::custom) + <$target_type>::deserialize($value.into_deserializer()).map_err(<$error>::custom) }; ($value:expr, $target_type:ty, $error:ty, $mapper:expr) => { @@ -49,8 +48,7 @@ macro_rules! extract_field { macro_rules! parse_data_json { ($in:ident, $error:ty) => { - serde_json::Value::deserialize($in.into_deserializer()) - .map_err(<$error>::custom) + serde_json::Value::deserialize($in.into_deserializer()).map_err(<$error>::custom) }; } @@ -69,10 +67,11 @@ macro_rules! parse_json_data_base64 { macro_rules! parse_data_base64 { ($in:ident, $error:ty) => { - parse_field!($in, String, $error) - .and_then(|s| base64::decode(&s).map_err(|e| { + parse_field!($in, String, $error).and_then(|s| { + base64::decode(&s).map_err(|e| { <$error>::invalid_value(serde::de::Unexpected::Str(&s), &e.to_string().as_str()) - })) + }) + }) }; } @@ -86,19 +85,20 @@ pub(crate) trait EventFormatDeserializer { map: &mut Map<String, Value>, ) -> Result<Option<Data>, E>; - fn deserialize_event<E: serde::de::Error>( - mut map: Map<String, Value>, - ) -> Result<Event, E> { + fn deserialize_event<E: serde::de::Error>(mut map: Map<String, Value>) -> Result<Event, E> { let attributes = Self::deserialize_attributes(&mut map)?; let data = Self::deserialize_data( - attributes - .datacontenttype() - .unwrap_or("application/json"), + attributes.datacontenttype().unwrap_or("application/json"), &mut map, )?; let extensions = map .into_iter() - .map(|(k, v)| Ok((k, ExtensionValue::deserialize(v.into_deserializer()).map_err(E::custom)?))) + .map(|(k, v)| { + Ok(( + k, + ExtensionValue::deserialize(v.into_deserializer()).map_err(E::custom)?, + )) + }) .collect::<Result<HashMap<String, ExtensionValue>, E>>()?; Ok(Event { @@ -124,10 +124,11 @@ impl<'de> Deserialize<'de> for Event { D: Deserializer<'de>, { let root_value = Value::deserialize(deserializer)?; - let mut map: Map<String, Value> = Map::deserialize(root_value.into_deserializer()) - .map_err(D::Error::custom)?; + let mut map: Map<String, Value> = + Map::deserialize(root_value.into_deserializer()).map_err(D::Error::custom)?; - match extract_field!(map, "specversion", String, <D as Deserializer<'de>>::Error)?.as_str() { + match extract_field!(map, "specversion", String, <D as Deserializer<'de>>::Error)?.as_str() + { "0.3" => EventFormatDeserializerV03::deserialize_event(map), "1.0" => EventFormatDeserializerV10::deserialize_event(map), s => Err(D::Error::unknown_variant( diff --git a/src/event/v03/format.rs b/src/event/v03/format.rs index 339688a2..e735e10c 100644 --- a/src/event/v03/format.rs +++ b/src/event/v03/format.rs @@ -5,7 +5,7 @@ use chrono::{DateTime, Utc}; use serde::de::IntoDeserializer; use serde::ser::SerializeMap; use serde::{Deserialize, Serializer}; -use serde_json::{Value, Map}; +use serde_json::{Map, Value}; use std::collections::HashMap; use url::Url; @@ -20,11 +20,13 @@ impl crate::event::format::EventFormatDeserializer for EventFormatDeserializer { ty: extract_field!(map, "type", String, E)?, source: extract_field!(map, "source", String, E, |s: String| Url::parse(&s))?, datacontenttype: extract_optional_field!(map, "datacontenttype", String, E)?, - schemaurl: extract_optional_field!(map, "schemaurl", String, E, |s: String| Url::parse(&s))?, + schemaurl: extract_optional_field!(map, "schemaurl", String, E, |s: String| { + Url::parse(&s) + })?, subject: extract_optional_field!(map, "subject", String, E)?, - time: extract_optional_field!(map, "time", String, E, - |s: String| DateTime::parse_from_rfc3339(&s).map(DateTime::<Utc>::from) - )?, + time: extract_optional_field!(map, "time", String, E, |s: String| { + DateTime::parse_from_rfc3339(&s).map(DateTime::<Utc>::from) + })?, })) } diff --git a/src/event/v10/format.rs b/src/event/v10/format.rs index 47a1a5df..df624639 100644 --- a/src/event/v10/format.rs +++ b/src/event/v10/format.rs @@ -5,9 +5,9 @@ use chrono::{DateTime, Utc}; use serde::de::IntoDeserializer; use serde::ser::SerializeMap; use serde::{Deserialize, Serializer}; +use serde_json::{Map, Value}; use std::collections::HashMap; use url::Url; -use serde_json::{Map, Value}; pub(crate) struct EventFormatDeserializer {} @@ -20,11 +20,13 @@ impl crate::event::format::EventFormatDeserializer for EventFormatDeserializer { ty: extract_field!(map, "type", String, E)?, source: extract_field!(map, "source", String, E, |s: String| Url::parse(&s))?, datacontenttype: extract_optional_field!(map, "datacontenttype", String, E)?, - dataschema: extract_optional_field!(map, "dataschema", String, E, |s: String| Url::parse(&s))?, + dataschema: extract_optional_field!(map, "dataschema", String, E, |s: String| { + Url::parse(&s) + })?, subject: extract_optional_field!(map, "subject", String, E)?, - time: extract_optional_field!(map, "time", String, E, - |s: String| DateTime::parse_from_rfc3339(&s).map(DateTime::<Utc>::from) - )?, + time: extract_optional_field!(map, "time", String, E, |s: String| { + DateTime::parse_from_rfc3339(&s).map(DateTime::<Utc>::from) + })?, })) }