Skip to content

V0.3 implementation #24

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 9 commits into from
Apr 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions src/event/attributes.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use super::SpecVersion;
use crate::event::AttributesV10;
use super::{AttributesV03, AttributesV10, SpecVersion};
use chrono::{DateTime, Utc};

/// Trait to get [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes).
Expand Down Expand Up @@ -30,61 +29,75 @@ pub trait AttributesWriter {
fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>);
}

pub(crate) trait AttributesConverter {
fn into_v03(self) -> AttributesV03;
fn into_v10(self) -> AttributesV10;
}

pub(crate) trait DataAttributesWriter {
fn set_datacontenttype(&mut self, datacontenttype: Option<impl Into<String>>);
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>);
}

#[derive(PartialEq, Debug, Clone)]
pub enum Attributes {
V03(AttributesV03),
V10(AttributesV10),
}

impl AttributesReader for Attributes {
fn get_id(&self) -> &str {
match self {
Attributes::V03(a) => a.get_id(),
Attributes::V10(a) => a.get_id(),
}
}

fn get_source(&self) -> &str {
match self {
Attributes::V03(a) => a.get_source(),
Attributes::V10(a) => a.get_source(),
}
}

fn get_specversion(&self) -> SpecVersion {
match self {
Attributes::V03(a) => a.get_specversion(),
Attributes::V10(a) => a.get_specversion(),
}
}

fn get_type(&self) -> &str {
match self {
Attributes::V03(a) => a.get_type(),
Attributes::V10(a) => a.get_type(),
}
}

fn get_datacontenttype(&self) -> Option<&str> {
match self {
Attributes::V03(a) => a.get_datacontenttype(),
Attributes::V10(a) => a.get_datacontenttype(),
}
}

fn get_dataschema(&self) -> Option<&str> {
match self {
Attributes::V03(a) => a.get_dataschema(),
Attributes::V10(a) => a.get_dataschema(),
}
}

fn get_subject(&self) -> Option<&str> {
match self {
Attributes::V03(a) => a.get_subject(),
Attributes::V10(a) => a.get_subject(),
}
}

fn get_time(&self) -> Option<&DateTime<Utc>> {
match self {
Attributes::V03(a) => a.get_time(),
Attributes::V10(a) => a.get_time(),
}
}
Expand All @@ -93,30 +106,35 @@ impl AttributesReader for Attributes {
impl AttributesWriter for Attributes {
fn set_id(&mut self, id: impl Into<String>) {
match self {
Attributes::V03(a) => a.set_id(id),
Attributes::V10(a) => a.set_id(id),
}
}

fn set_source(&mut self, source: impl Into<String>) {
match self {
Attributes::V03(a) => a.set_source(source),
Attributes::V10(a) => a.set_source(source),
}
}

fn set_type(&mut self, ty: impl Into<String>) {
match self {
Attributes::V03(a) => a.set_type(ty),
Attributes::V10(a) => a.set_type(ty),
}
}

fn set_subject(&mut self, subject: Option<impl Into<String>>) {
match self {
Attributes::V03(a) => a.set_subject(subject),
Attributes::V10(a) => a.set_subject(subject),
}
}

fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) {
match self {
Attributes::V03(a) => a.set_time(time),
Attributes::V10(a) => a.set_time(time),
}
}
Expand All @@ -125,13 +143,30 @@ impl AttributesWriter for Attributes {
impl DataAttributesWriter for Attributes {
fn set_datacontenttype(&mut self, datacontenttype: Option<impl Into<String>>) {
match self {
Attributes::V03(a) => a.set_datacontenttype(datacontenttype),
Attributes::V10(a) => a.set_datacontenttype(datacontenttype),
}
}

fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
match self {
Attributes::V03(a) => a.set_dataschema(dataschema),
Attributes::V10(a) => a.set_dataschema(dataschema),
}
}
}

impl Attributes {
pub fn into_v10(self) -> Self {
match self {
Attributes::V03(v03) => Attributes::V10(v03.into_v10()),
_ => self,
}
}
pub fn into_v03(self) -> Self {
match self {
Attributes::V10(v10) => Attributes::V03(v10.into_v03()),
_ => self,
}
}
}
12 changes: 11 additions & 1 deletion src/event/builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::EventBuilderV10;
use super::{EventBuilderV03, EventBuilderV10};

/// Builder to create [`Event`]:
/// ```
Expand All @@ -14,8 +14,18 @@ use super::EventBuilderV10;
pub struct EventBuilder {}

impl EventBuilder {
/// Creates a new builder for latest CloudEvents version
pub fn new() -> EventBuilderV10 {
return Self::v10();
}

/// Creates a new builder for CloudEvents V1.0
pub fn v10() -> EventBuilderV10 {
return EventBuilderV10::new();
}

/// Creates a new builder for CloudEvents V0.3
pub fn v03() -> EventBuilderV03 {
return EventBuilderV03::new();
}
}
15 changes: 12 additions & 3 deletions src/event/data.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use std::convert::{Into, TryFrom};

/// Event [data attribute](https://github.com/cloudevents/spec/blob/master/spec.md#event-data) representation
///
#[derive(Debug, PartialEq, Clone)]
pub enum Data {
/// Event has a binary payload
Binary(Vec<u8>),
/// Event has a non-json string payload
String(String),
/// Event has a json payload
Json(serde_json::Value),
}

Expand All @@ -30,6 +33,10 @@ impl Data {
}
}

pub(crate) fn is_json_content_type(ct: &str) -> bool {
ct == "application/json" || ct == "text/json" || ct.ends_with("+json")
}

impl Into<Data> for serde_json::Value {
fn into(self) -> Data {
Data::Json(self)
Expand All @@ -44,7 +51,7 @@ impl Into<Data> for Vec<u8> {

impl Into<Data> for String {
fn into(self) -> Data {
Data::Json(self.into())
Data::String(self)
}
}

Expand All @@ -55,6 +62,7 @@ impl TryFrom<Data> for serde_json::Value {
match value {
Data::Binary(v) => Ok(serde_json::from_slice(&v)?),
Data::Json(v) => Ok(v),
Data::String(s) => Ok(serde_json::from_str(&s)?),
}
}
}
Expand All @@ -66,6 +74,7 @@ impl TryFrom<Data> for Vec<u8> {
match value {
Data::Binary(v) => Ok(serde_json::from_slice(&v)?),
Data::Json(v) => Ok(serde_json::to_vec(&v)?),
Data::String(s) => Ok(s.into_bytes()),
}
}
}
Expand All @@ -76,8 +85,8 @@ impl TryFrom<Data> for String {
fn try_from(value: Data) -> Result<Self, Self::Error> {
match value {
Data::Binary(v) => Ok(String::from_utf8(v)?),
Data::Json(serde_json::Value::String(s)) => Ok(s), // Return the string without quotes
Data::Json(v) => Ok(v.to_string()),
Data::String(s) => Ok(s),
}
}
}
7 changes: 7 additions & 0 deletions src/event/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ pub use event::Event;
pub use extensions::ExtensionValue;
pub use spec_version::SpecVersion;

mod v03;

pub use v03::Attributes as AttributesV03;
pub use v03::EventBuilder as EventBuilderV03;
pub(crate) use v03::EventDeserializer as EventDeserializerV03;
pub(crate) use v03::EventSerializer as EventSerializerV03;

mod v10;

pub use v10::Attributes as AttributesV10;
Expand Down
107 changes: 78 additions & 29 deletions src/event/serde.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use super::{Attributes, Data, Event, EventDeserializerV10, EventSerializerV10};
use crate::event::ExtensionValue;
use super::{
Attributes, Data, Event, EventDeserializerV03, EventDeserializerV10, EventSerializerV03,
EventSerializerV10,
};
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};

const SPEC_VERSIONS: [&'static str; 1] = ["1.0"];
const SPEC_VERSIONS: [&'static str; 2] = ["0.3", "1.0"];

macro_rules! parse_optional_field {
($map:ident, $name:literal, $value_variant:ident, $error:ty) => {
Expand All @@ -28,16 +31,78 @@ macro_rules! parse_field {
};
}

macro_rules! parse_data_json {
($in:ident, $error:ty) => {
Ok(serde_json::Value::deserialize($in.into_deserializer())
.map_err(|e| <$error>::custom(e))?)
};
}

macro_rules! parse_data_string {
($in:ident, $error:ty) => {
match $in {
Value::String(s) => Ok(s),
other => Err(E::invalid_type(
crate::event::serde::value_to_unexpected(&other),
&"a string",
)),
}
};
}

macro_rules! parse_json_data_base64 {
($in:ident, $error:ty) => {{
let data = parse_data_base64!($in, $error)?;
serde_json::from_slice(&data).map_err(|e| <$error>::custom(e))
}};
}

macro_rules! parse_data_base64 {
($in:ident, $error:ty) => {
match $in {
Value::String(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::serde::value_to_unexpected(&other),
&"a string",
)),
}
};
}

pub(crate) trait EventDeserializer {
fn deserialize_attributes<E: serde::de::Error>(
&self,
map: &mut BTreeMap<String, Value>,
) -> Result<Attributes, E>;

fn deserialize_data<E: serde::de::Error>(
&self,
content_type: &str,
map: &mut BTreeMap<String, Value>,
) -> Result<Option<Data>, E>;

fn deserialize_event<E: serde::de::Error>(
mut map: BTreeMap<String, Value>,
) -> Result<Event, E> {
let attributes = Self::deserialize_attributes(&mut map)?;
let data = Self::deserialize_data(
attributes
.get_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| E::custom(e))?;

Ok(Event {
attributes,
data,
extensions,
})
}
}

pub(crate) trait EventSerializer<S: Serializer, A: Sized> {
Expand Down Expand Up @@ -67,30 +132,11 @@ impl<'de> Deserialize<'de> for Event {
})
.collect::<Result<BTreeMap<String, Value>, <D as Deserializer<'de>>::Error>>()?;

let event_deserializer =
match parse_field!(map, "specversion", String, <D as Deserializer<'de>>::Error)?
.as_str()
{
"1.0" => Ok(EventDeserializerV10 {}),
s => Err(<D as Deserializer<'de>>::Error::unknown_variant(
s,
&SPEC_VERSIONS,
)),
}?;

let attributes = event_deserializer.deserialize_attributes(&mut map)?;
let data = event_deserializer.deserialize_data(&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| <D as Deserializer<'de>>::Error::custom(e))?;

Ok(Event {
attributes,
data,
extensions,
})
match parse_field!(map, "specversion", String, <D as Deserializer<'de>>::Error)?.as_str() {
"0.3" => EventDeserializerV03::deserialize_event(map),
"1.0" => EventDeserializerV10::deserialize_event(map),
s => Err(D::Error::unknown_variant(s, &SPEC_VERSIONS)),
}
}
}

Expand All @@ -100,6 +146,9 @@ impl Serialize for Event {
S: Serializer,
{
match &self.attributes {
Attributes::V03(a) => {
EventSerializerV03::serialize(a, &self.data, &self.extensions, serializer)
}
Attributes::V10(a) => {
EventSerializerV10::serialize(a, &self.data, &self.extensions, serializer)
}
Expand Down
Loading