Introduced URL support (#27)

* Introduced url library

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Fixed lock

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Integrated changes with url library

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>

* Cargo fmt

Signed-off-by: Francesco Guardiani <francescoguard@gmail.com>
This commit is contained in:
Francesco Guardiani 2020-04-29 21:40:37 +02:00 committed by GitHub
parent 4c25539abf
commit 9b2f26223f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 231 additions and 118 deletions

60
Cargo.lock generated
View File

@ -53,6 +53,7 @@ dependencies = [
"serde-value",
"serde_json",
"snafu",
"url",
"uuid",
]
@ -94,6 +95,17 @@ dependencies = [
"winutil",
]
[[package]]
name = "idna"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9"
dependencies = [
"matches",
"unicode-bidi",
"unicode-normalization",
]
[[package]]
name = "itoa"
version = "0.4.5"
@ -106,6 +118,12 @@ version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
[[package]]
name = "matches"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08"
[[package]]
name = "num-integer"
version = "0.1.42"
@ -134,6 +152,12 @@ dependencies = [
"num-traits",
]
[[package]]
name = "percent-encoding"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
name = "ppv-lite86"
version = "0.2.6"
@ -289,6 +313,12 @@ dependencies = [
"serde",
]
[[package]]
name = "smallvec"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7cb5678e1615754284ec264d9bb5b4c27d2018577fd90ac0ceb578591ed5ee4"
[[package]]
name = "snafu"
version = "0.6.6"
@ -332,12 +362,42 @@ dependencies = [
"winapi",
]
[[package]]
name = "unicode-bidi"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5"
dependencies = [
"matches",
]
[[package]]
name = "unicode-normalization"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4"
dependencies = [
"smallvec",
]
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
[[package]]
name = "url"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb"
dependencies = [
"idna",
"matches",
"percent-encoding",
"serde",
]
[[package]]
name = "uuid"
version = "0.8.1"

View File

@ -19,6 +19,7 @@ delegate = "^0.4"
uuid = { version = "^0.8", features = ["serde", "v4"] }
hostname = "^0.1"
base64 = "^0.12"
url = { version = "^2.1", features = ["serde"] }
snafu = "^0.6"
[dev-dependencies]

View File

@ -1,12 +1,13 @@
use super::{AttributesV03, AttributesV10, SpecVersion};
use chrono::{DateTime, Utc};
use url::Url;
/// Trait to get [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes).
pub trait AttributesReader {
/// Get the [id](https://github.com/cloudevents/spec/blob/master/spec.md#id).
fn get_id(&self) -> &str;
/// Get the [source](https://github.com/cloudevents/spec/blob/master/spec.md#source-1).
fn get_source(&self) -> &str;
fn get_source(&self) -> &Url;
/// Get the [specversion](https://github.com/cloudevents/spec/blob/master/spec.md#specversion).
fn get_specversion(&self) -> SpecVersion;
/// Get the [type](https://github.com/cloudevents/spec/blob/master/spec.md#type).
@ -14,7 +15,7 @@ pub trait AttributesReader {
/// Get the [datacontenttype](https://github.com/cloudevents/spec/blob/master/spec.md#datacontenttype).
fn get_datacontenttype(&self) -> Option<&str>;
/// Get the [dataschema](https://github.com/cloudevents/spec/blob/master/spec.md#dataschema).
fn get_dataschema(&self) -> Option<&str>;
fn get_dataschema(&self) -> Option<&Url>;
/// Get the [subject](https://github.com/cloudevents/spec/blob/master/spec.md#subject).
fn get_subject(&self) -> Option<&str>;
/// Get the [time](https://github.com/cloudevents/spec/blob/master/spec.md#time).
@ -23,7 +24,7 @@ pub trait AttributesReader {
pub trait AttributesWriter {
fn set_id(&mut self, id: impl Into<String>);
fn set_source(&mut self, source: impl Into<String>);
fn set_source(&mut self, source: impl Into<Url>);
fn set_type(&mut self, ty: impl Into<String>);
fn set_subject(&mut self, subject: Option<impl Into<String>>);
fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>);
@ -36,7 +37,7 @@ pub(crate) trait AttributesConverter {
pub(crate) trait DataAttributesWriter {
fn set_datacontenttype(&mut self, datacontenttype: Option<impl Into<String>>);
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>);
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>);
}
#[derive(PartialEq, Debug, Clone)]
@ -53,7 +54,7 @@ impl AttributesReader for Attributes {
}
}
fn get_source(&self) -> &str {
fn get_source(&self) -> &Url {
match self {
Attributes::V03(a) => a.get_source(),
Attributes::V10(a) => a.get_source(),
@ -81,7 +82,7 @@ impl AttributesReader for Attributes {
}
}
fn get_dataschema(&self) -> Option<&str> {
fn get_dataschema(&self) -> Option<&Url> {
match self {
Attributes::V03(a) => a.get_dataschema(),
Attributes::V10(a) => a.get_dataschema(),
@ -111,7 +112,7 @@ impl AttributesWriter for Attributes {
}
}
fn set_source(&mut self, source: impl Into<String>) {
fn set_source(&mut self, source: impl Into<Url>) {
match self {
Attributes::V03(a) => a.set_source(source),
Attributes::V10(a) => a.set_source(source),
@ -148,7 +149,7 @@ impl DataAttributesWriter for Attributes {
}
}
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) {
match self {
Attributes::V03(a) => a.set_dataschema(dataschema),
Attributes::V10(a) => a.set_dataschema(dataschema),

View File

@ -4,10 +4,11 @@ use super::{EventBuilderV03, EventBuilderV10};
/// ```
/// use cloudevents::EventBuilder;
/// use chrono::Utc;
/// use url::Url;
///
/// let event = EventBuilder::v10()
/// .id("my_event.my_application")
/// .source("http://localhost:8080")
/// .source(Url::parse("http://localhost:8080").unwrap())
/// .time(Utc::now())
/// .build();
/// ```

View File

@ -7,6 +7,7 @@ use chrono::{DateTime, Utc};
use delegate::delegate;
use std::collections::HashMap;
use std::convert::TryFrom;
use url::Url;
/// Data structure that represents a [CloudEvent](https://github.com/cloudevents/spec/blob/master/spec.md).
/// It provides methods to get the attributes through [`AttributesReader`]
@ -43,11 +44,11 @@ impl AttributesReader for Event {
delegate! {
to self.attributes {
fn get_id(&self) -> &str;
fn get_source(&self) -> &str;
fn get_source(&self) -> &Url;
fn get_specversion(&self) -> SpecVersion;
fn get_type(&self) -> &str;
fn get_datacontenttype(&self) -> Option<&str>;
fn get_dataschema(&self) -> Option<&str>;
fn get_dataschema(&self) -> Option<&Url>;
fn get_subject(&self) -> Option<&str>;
fn get_time(&self) -> Option<&DateTime<Utc>>;
}
@ -58,7 +59,7 @@ impl AttributesWriter for Event {
delegate! {
to self.attributes {
fn set_id(&mut self, id: impl Into<String>);
fn set_source(&mut self, source: impl Into<String>);
fn set_source(&mut self, source: impl Into<Url>);
fn set_type(&mut self, ty: impl Into<String>);
fn set_subject(&mut self, subject: Option<impl Into<String>>);
fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>);
@ -79,7 +80,7 @@ impl Default for Event {
impl Event {
pub fn remove_data(&mut self) {
self.data = None;
self.attributes.set_dataschema(None as Option<String>);
self.attributes.set_dataschema(None as Option<Url>);
self.attributes.set_datacontenttype(None as Option<String>);
}
@ -95,7 +96,7 @@ impl Event {
/// ```
pub fn write_data(&mut self, datacontenttype: impl Into<String>, data: impl Into<Data>) {
self.attributes.set_datacontenttype(Some(datacontenttype));
self.attributes.set_dataschema(None as Option<&str>);
self.attributes.set_dataschema(None as Option<Url>);
self.data = Some(data.into());
}
@ -105,14 +106,19 @@ impl Event {
/// use cloudevents::Event;
/// use serde_json::json;
/// use std::convert::Into;
/// use url::Url;
///
/// let mut e = Event::default();
/// e.write_data_with_schema("application/json", "http://myapplication.com/schema", json!({}))
/// e.write_data_with_schema(
/// "application/json",
/// Url::parse("http://myapplication.com/schema").unwrap(),
/// json!({})
/// )
/// ```
pub fn write_data_with_schema(
&mut self,
datacontenttype: impl Into<String>,
dataschema: impl Into<String>,
dataschema: impl Into<Url>,
data: impl Into<Data>,
) {
self.attributes.set_datacontenttype(Some(datacontenttype));
@ -186,14 +192,17 @@ mod tests {
let mut e = Event::default();
e.write_data_with_schema(
"application/json",
"http://localhost:8080/schema",
Url::parse("http://localhost:8080/schema").unwrap(),
expected_data.clone(),
);
let data: serde_json::Value = e.try_get_data().unwrap().unwrap();
assert_eq!(expected_data, data);
assert_eq!("application/json", e.get_datacontenttype().unwrap());
assert_eq!("http://localhost:8080/schema", e.get_dataschema().unwrap())
assert_eq!(
&Url::parse("http://localhost:8080/schema").unwrap(),
e.get_dataschema().unwrap()
)
}
#[test]

View File

@ -20,6 +20,23 @@ macro_rules! parse_optional_field {
})
.transpose()
};
($map:ident, $name:literal, $value_variant:ident, $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::serde::value_to_unexpected(&Value::$value_variant(v)),
&e.to_string().as_str(),
)
}),
other => Err(<$error>::invalid_type(
crate::event::serde::value_to_unexpected(&other),
&stringify!($value_variant),
)),
})
.transpose()
};
}
macro_rules! parse_field {
@ -27,6 +44,11 @@ macro_rules! parse_field {
parse_optional_field!($map, $name, $value_variant, $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)?
.ok_or_else(|| <$error>::missing_field($name))
};
}
macro_rules! parse_data_json {

View File

@ -3,15 +3,16 @@ use crate::event::AttributesV10;
use crate::event::{AttributesReader, AttributesWriter, SpecVersion};
use chrono::{DateTime, Utc};
use hostname::get_hostname;
use url::Url;
use uuid::Uuid;
#[derive(PartialEq, Debug, Clone)]
pub struct Attributes {
pub(crate) id: String,
pub(crate) ty: String,
pub(crate) source: String,
pub(crate) source: Url,
pub(crate) datacontenttype: Option<String>,
pub(crate) schemaurl: Option<String>,
pub(crate) schemaurl: Option<Url>,
pub(crate) subject: Option<String>,
pub(crate) time: Option<DateTime<Utc>>,
}
@ -21,7 +22,7 @@ impl AttributesReader for Attributes {
&self.id
}
fn get_source(&self) -> &str {
fn get_source(&self) -> &Url {
&self.source
}
@ -34,24 +35,15 @@ impl AttributesReader for Attributes {
}
fn get_datacontenttype(&self) -> Option<&str> {
match self.datacontenttype.as_ref() {
Some(s) => Some(&s),
None => None,
}
self.datacontenttype.as_deref()
}
fn get_dataschema(&self) -> Option<&str> {
match self.schemaurl.as_ref() {
Some(s) => Some(&s),
None => None,
}
fn get_dataschema(&self) -> Option<&Url> {
self.schemaurl.as_ref()
}
fn get_subject(&self) -> Option<&str> {
match self.subject.as_ref() {
Some(s) => Some(&s),
None => None,
}
self.subject.as_deref()
}
fn get_time(&self) -> Option<&DateTime<Utc>> {
@ -64,7 +56,7 @@ impl AttributesWriter for Attributes {
self.id = id.into()
}
fn set_source(&mut self, source: impl Into<String>) {
fn set_source(&mut self, source: impl Into<Url>) {
self.source = source.into()
}
@ -86,7 +78,7 @@ impl DataAttributesWriter for Attributes {
self.datacontenttype = datacontenttype.map(Into::into)
}
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) {
self.schemaurl = dataschema.map(Into::into)
}
}
@ -96,7 +88,14 @@ impl Default for Attributes {
Attributes {
id: Uuid::new_v4().to_string(),
ty: "type".to_string(),
source: get_hostname().unwrap_or("http://localhost/".to_string()),
source: Url::parse(
format!(
"http://{}",
get_hostname().unwrap_or("localhost".to_string())
)
.as_ref(),
)
.unwrap(),
datacontenttype: None,
schemaurl: None,
subject: None,

View File

@ -2,6 +2,7 @@ use super::Attributes as AttributesV03;
use crate::event::{Attributes, AttributesWriter, Data, Event, ExtensionValue};
use chrono::{DateTime, Utc};
use std::collections::HashMap;
use url::Url;
pub struct EventBuilder {
event: Event,
@ -33,7 +34,7 @@ impl EventBuilder {
return self;
}
pub fn source(mut self, source: impl Into<String>) -> Self {
pub fn source(mut self, source: impl Into<Url>) -> Self {
self.event.set_source(source);
return self;
}
@ -70,7 +71,7 @@ impl EventBuilder {
pub fn data_with_schema(
mut self,
datacontenttype: impl Into<String>,
schemaurl: impl Into<String>,
schemaurl: impl Into<Url>,
data: impl Into<Data>,
) -> Self {
self.event
@ -91,31 +92,31 @@ mod tests {
#[test]
fn build_event() {
let id = "aaa";
let source = "http://localhost:8080";
let source = Url::parse("http://localhost:8080").unwrap();
let ty = "bbb";
let subject = "francesco";
let time: DateTime<Utc> = Utc::now();
let extension_name = "ext";
let extension_value = 10i64;
let content_type = "application/json";
let schema = "http://localhost:8080/schema";
let schema = Url::parse("http://localhost:8080/schema").unwrap();
let data = serde_json::json!({
"hello": "world"
});
let event = EventBuilder::new()
.id(id)
.source(source)
.source(source.clone())
.ty(ty)
.subject(subject)
.time(time)
.extension(extension_name, extension_value)
.data_with_schema(content_type, schema, data.clone())
.data_with_schema(content_type, schema.clone(), data.clone())
.build();
assert_eq!(SpecVersion::V03, event.get_specversion());
assert_eq!(id, event.get_id());
assert_eq!(source, event.get_source());
assert_eq!(source, event.get_source().clone());
assert_eq!(ty, event.get_type());
assert_eq!(subject, event.get_subject().unwrap());
assert_eq!(time, event.get_time().unwrap().clone());
@ -124,7 +125,7 @@ mod tests {
event.get_extension(extension_name).unwrap().clone()
);
assert_eq!(content_type, event.get_datacontenttype().unwrap());
assert_eq!(schema, event.get_dataschema().unwrap());
assert_eq!(schema, event.get_dataschema().unwrap().clone());
let event_data: serde_json::Value = event.try_get_data().unwrap().unwrap();
assert_eq!(data, event_data);

View File

@ -42,9 +42,9 @@ impl crate::event::deserializer::AttributesSerializer for super::Attributes {
match name {
"id" => self.id = value.to_string(),
"type" => self.ty = value.to_string(),
"source" => self.source = value.to_string(),
"source" => self.source = value.try_into()?,
"datacontenttype" => self.datacontenttype = Some(value.to_string()),
"schemaurl" => self.schemaurl = Some(value.to_string()),
"schemaurl" => self.schemaurl = Some(value.try_into()?),
"subject" => self.subject = Some(value.to_string()),
"time" => self.time = Some(value.try_into()?),
_ => {

View File

@ -2,11 +2,12 @@ use super::Attributes;
use crate::event::data::is_json_content_type;
use crate::event::{Data, ExtensionValue};
use chrono::{DateTime, Utc};
use serde::de::{IntoDeserializer, Unexpected};
use serde::de::IntoDeserializer;
use serde::ser::SerializeMap;
use serde::{Deserialize, Serializer};
use serde_value::Value;
use std::collections::{BTreeMap, HashMap};
use url::Url;
pub(crate) struct EventDeserializer {}
@ -17,19 +18,14 @@ impl crate::event::serde::EventDeserializer for EventDeserializer {
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)?,
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)?,
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)?
.map(|s| match DateTime::parse_from_rfc3339(&s) {
Ok(d) => Ok(DateTime::<Utc>::from(d)),
Err(e) => Err(E::invalid_value(
Unexpected::Str(&s),
&e.to_string().as_str(),
)),
})
.transpose()?,
time: parse_optional_field!(map, "time", String, E, |s| DateTime::parse_from_rfc3339(
s
)
.map(DateTime::<Utc>::from))?,
}))
}

View File

@ -2,15 +2,16 @@ use crate::event::attributes::{AttributesConverter, DataAttributesWriter};
use crate::event::{AttributesReader, AttributesV03, AttributesWriter, SpecVersion};
use chrono::{DateTime, Utc};
use hostname::get_hostname;
use url::Url;
use uuid::Uuid;
#[derive(PartialEq, Debug, Clone)]
pub struct Attributes {
pub(crate) id: String,
pub(crate) ty: String,
pub(crate) source: String,
pub(crate) source: Url,
pub(crate) datacontenttype: Option<String>,
pub(crate) dataschema: Option<String>,
pub(crate) dataschema: Option<Url>,
pub(crate) subject: Option<String>,
pub(crate) time: Option<DateTime<Utc>>,
}
@ -20,7 +21,7 @@ impl AttributesReader for Attributes {
&self.id
}
fn get_source(&self) -> &str {
fn get_source(&self) -> &Url {
&self.source
}
@ -33,24 +34,15 @@ impl AttributesReader for Attributes {
}
fn get_datacontenttype(&self) -> Option<&str> {
match self.datacontenttype.as_ref() {
Some(s) => Some(&s),
None => None,
}
self.datacontenttype.as_deref()
}
fn get_dataschema(&self) -> Option<&str> {
match self.dataschema.as_ref() {
Some(s) => Some(&s),
None => None,
}
fn get_dataschema(&self) -> Option<&Url> {
self.dataschema.as_ref()
}
fn get_subject(&self) -> Option<&str> {
match self.subject.as_ref() {
Some(s) => Some(&s),
None => None,
}
self.subject.as_deref()
}
fn get_time(&self) -> Option<&DateTime<Utc>> {
@ -63,7 +55,7 @@ impl AttributesWriter for Attributes {
self.id = id.into()
}
fn set_source(&mut self, source: impl Into<String>) {
fn set_source(&mut self, source: impl Into<Url>) {
self.source = source.into()
}
@ -85,7 +77,7 @@ impl DataAttributesWriter for Attributes {
self.datacontenttype = datacontenttype.map(Into::into)
}
fn set_dataschema(&mut self, dataschema: Option<impl Into<String>>) {
fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) {
self.dataschema = dataschema.map(Into::into)
}
}
@ -95,7 +87,14 @@ impl Default for Attributes {
Attributes {
id: Uuid::new_v4().to_string(),
ty: "type".to_string(),
source: get_hostname().unwrap_or("http://localhost/".to_string()),
source: Url::parse(
format!(
"http://{}",
get_hostname().unwrap_or("localhost".to_string())
)
.as_ref(),
)
.unwrap(),
datacontenttype: None,
dataschema: None,
subject: None,

View File

@ -2,6 +2,7 @@ use super::Attributes as AttributesV10;
use crate::event::{Attributes, AttributesWriter, Data, Event, ExtensionValue};
use chrono::{DateTime, Utc};
use std::collections::HashMap;
use url::Url;
pub struct EventBuilder {
event: Event,
@ -33,7 +34,7 @@ impl EventBuilder {
return self;
}
pub fn source(mut self, source: impl Into<String>) -> Self {
pub fn source(mut self, source: impl Into<Url>) -> Self {
self.event.set_source(source);
return self;
}
@ -70,7 +71,7 @@ impl EventBuilder {
pub fn data_with_schema(
mut self,
datacontenttype: impl Into<String>,
dataschema: impl Into<String>,
dataschema: impl Into<Url>,
data: impl Into<Data>,
) -> Self {
self.event
@ -91,31 +92,31 @@ mod tests {
#[test]
fn build_event() {
let id = "aaa";
let source = "http://localhost:8080";
let source = Url::parse("http://localhost:8080").unwrap();
let ty = "bbb";
let subject = "francesco";
let time: DateTime<Utc> = Utc::now();
let extension_name = "ext";
let extension_value = 10i64;
let content_type = "application/json";
let schema = "http://localhost:8080/schema";
let schema = Url::parse("http://localhost:8080/schema").unwrap();
let data = serde_json::json!({
"hello": "world"
});
let event = EventBuilder::new()
.id(id)
.source(source)
.source(source.clone())
.ty(ty)
.subject(subject)
.time(time)
.extension(extension_name, extension_value)
.data_with_schema(content_type, schema, data.clone())
.data_with_schema(content_type, schema.clone(), data.clone())
.build();
assert_eq!(SpecVersion::V10, event.get_specversion());
assert_eq!(id, event.get_id());
assert_eq!(source, event.get_source());
assert_eq!(source, event.get_source().clone());
assert_eq!(ty, event.get_type());
assert_eq!(subject, event.get_subject().unwrap());
assert_eq!(time, event.get_time().unwrap().clone());
@ -124,7 +125,7 @@ mod tests {
event.get_extension(extension_name).unwrap().clone()
);
assert_eq!(content_type, event.get_datacontenttype().unwrap());
assert_eq!(schema, event.get_dataschema().unwrap());
assert_eq!(schema, event.get_dataschema().unwrap().clone());
let event_data: serde_json::Value = event.try_get_data().unwrap().unwrap();
assert_eq!(data, event_data);

View File

@ -42,9 +42,9 @@ impl crate::event::deserializer::AttributesSerializer for super::Attributes {
match name {
"id" => self.id = value.to_string(),
"type" => self.ty = value.to_string(),
"source" => self.source = value.to_string(),
"source" => self.source = value.try_into()?,
"datacontenttype" => self.datacontenttype = Some(value.to_string()),
"dataschema" => self.dataschema = Some(value.to_string()),
"dataschema" => self.dataschema = Some(value.try_into()?),
"subject" => self.subject = Some(value.to_string()),
"time" => self.time = Some(value.try_into()?),
_ => {

View File

@ -2,11 +2,12 @@ use super::Attributes;
use crate::event::data::is_json_content_type;
use crate::event::{Data, ExtensionValue};
use chrono::{DateTime, Utc};
use serde::de::{IntoDeserializer, Unexpected};
use serde::de::IntoDeserializer;
use serde::ser::SerializeMap;
use serde::{Deserialize, Serializer};
use serde_value::Value;
use std::collections::{BTreeMap, HashMap};
use url::Url;
pub(crate) struct EventDeserializer {}
@ -17,19 +18,14 @@ impl crate::event::serde::EventDeserializer for EventDeserializer {
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)?,
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)?,
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)?
.map(|s| match DateTime::parse_from_rfc3339(&s) {
Ok(d) => Ok(DateTime::<Utc>::from(d)),
Err(e) => Err(E::invalid_value(
Unexpected::Str(&s),
&e.to_string().as_str(),
)),
})
.transpose()?,
time: parse_optional_field!(map, "time", String, E, |s| DateTime::parse_from_rfc3339(
s
)
.map(DateTime::<Utc>::from))?,
}))
}

View File

@ -8,9 +8,12 @@ use std::io::Read;
pub enum Error {
#[snafu(display("Unrecognized attribute name: {}", name))]
UnrecognizedAttributeName { name: String },
#[snafu(display("Error while decoding base64: {}", source))]
#[snafu(display("Error while parsing a time string: {}", source))]
#[snafu(context(false))]
ParseTimeError { source: chrono::ParseError },
#[snafu(display("Error while parsing a url: {}", source))]
#[snafu(context(false))]
ParseUrlError { source: url::ParseError },
#[snafu(display("Error while decoding base64: {}", source))]
#[snafu(context(false))]
Base64DecodingError { source: base64::DecodeError },

View File

@ -1,14 +1,15 @@
use crate::event::ExtensionValue;
use chrono::{DateTime, Utc};
use std::convert::TryInto;
use url::Url;
pub enum MessageAttributeValue {
Boolean(bool),
Integer(i64),
String(String),
Binary(Vec<u8>),
Uri(String),
UriRef(String),
Uri(Url),
UriRef(Url),
DateTime(DateTime<Utc>),
}
@ -25,6 +26,18 @@ impl TryInto<DateTime<Utc>> for MessageAttributeValue {
}
}
impl TryInto<Url> for MessageAttributeValue {
type Error = super::Error;
fn try_into(self) -> Result<Url, Self::Error> {
match self {
MessageAttributeValue::Uri(u) => Ok(u),
MessageAttributeValue::UriRef(u) => Ok(u),
v => Ok(Url::parse(v.to_string().as_ref())?),
}
}
}
impl ToString for MessageAttributeValue {
fn to_string(&self) -> String {
match self {
@ -32,8 +45,8 @@ impl ToString for MessageAttributeValue {
MessageAttributeValue::Integer(i) => i.to_string(),
MessageAttributeValue::String(s) => s.clone(),
MessageAttributeValue::Binary(v) => base64::encode(v),
MessageAttributeValue::Uri(s) => s.clone(),
MessageAttributeValue::UriRef(s) => s.clone(),
MessageAttributeValue::Uri(u) => u.to_string(),
MessageAttributeValue::UriRef(u) => u.to_string(),
MessageAttributeValue::DateTime(d) => d.to_rfc3339(),
}
}

View File

@ -10,7 +10,7 @@ pub fn ty() -> String {
}
pub fn source() -> String {
"http://localhost".to_string()
"http://localhost/".to_string()
}
pub fn json_datacontenttype() -> String {

View File

@ -1,11 +1,13 @@
use super::*;
use cloudevents::{Event, EventBuilder};
use serde_json::{json, Value};
use std::convert::TryInto;
use url::Url;
pub fn minimal() -> Event {
EventBuilder::v03()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.build()
}
@ -26,7 +28,7 @@ pub fn full_no_data() -> Event {
EventBuilder::v03()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.subject(subject())
.time(time())
@ -61,14 +63,18 @@ pub fn full_json_data() -> Event {
EventBuilder::v03()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.subject(subject())
.time(time())
.extension(&string_ext_name, string_ext_value)
.extension(&bool_ext_name, bool_ext_value)
.extension(&int_ext_name, int_ext_value)
.data_with_schema(json_datacontenttype(), dataschema(), json_data())
.data_with_schema(
json_datacontenttype(),
Url::parse(dataschema().as_ref()).unwrap(),
json_data(),
)
.build()
}
@ -122,7 +128,7 @@ pub fn full_xml_string_data() -> Event {
EventBuilder::v03()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.subject(subject())
.time(time())
@ -140,7 +146,7 @@ pub fn full_xml_binary_data() -> Event {
EventBuilder::v03()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.subject(subject())
.time(time())

View File

@ -1,11 +1,12 @@
use super::*;
use cloudevents::{Event, EventBuilder};
use serde_json::{json, Value};
use url::Url;
pub fn minimal() -> Event {
EventBuilder::v10()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.build()
}
@ -26,7 +27,7 @@ pub fn full_no_data() -> Event {
EventBuilder::v10()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.subject(subject())
.time(time())
@ -61,14 +62,18 @@ pub fn full_json_data() -> Event {
EventBuilder::v10()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.subject(subject())
.time(time())
.extension(&string_ext_name, string_ext_value)
.extension(&bool_ext_name, bool_ext_value)
.extension(&int_ext_name, int_ext_value)
.data_with_schema(json_datacontenttype(), dataschema(), json_data())
.data_with_schema(
json_datacontenttype(),
Url::parse(dataschema().as_ref()).unwrap(),
json_data(),
)
.build()
}
@ -121,7 +126,7 @@ pub fn full_xml_string_data() -> Event {
EventBuilder::v10()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.subject(subject())
.time(time())
@ -139,7 +144,7 @@ pub fn full_xml_binary_data() -> Event {
EventBuilder::v10()
.id(id())
.source(source())
.source(Url::parse(source().as_ref()).unwrap())
.ty(ty())
.subject(subject())
.time(time())