diff --git a/.history/src/lib_20201107132414.rs b/.history/src/lib_20201107132414.rs new file mode 100644 index 00000000..e5314baf --- /dev/null +++ b/.history/src/lib_20201107132414.rs @@ -0,0 +1,60 @@ +//! This crate implements the [CloudEvents](https://cloudevents.io/) Spec for Rust. +//! +//! ``` +//! # use std::error::Error; +//! # fn main() -> Result<(), Box> { +//! use cloudevents::{EventBuilder, AttributesReader, EventBuilderV10}; +//! use chrono::{Utc, DateTime}; +//! use url::Url; +//! +//! let event = EventBuilderV10::new() +//! .id("my_event.my_application") +//! .source("http://localhost:8080") +//! .ty("example.demo") +//! .time(Utc::now()) +//! .build()?; +//! +//! println!("CloudEvent Id: {}", event.id()); +//! match event.time() { +//! Some(t) => println!("CloudEvent Time: {}", t), +//! None => println!("CloudEvent Time: None") +//! } +//! # Ok(()) +//! # } +//! ``` +//! +//! This crate includes: +//! +//! * The [`Event`] data structure, to represent CloudEvent (version 1.0 and 0.3) +//! * The [`EventBuilder`] trait and implementations, to create [`Event`] instances +//! * The implementation of [`serde::Serialize`] and [`serde::Deserialize`] for [`Event`] to serialize/deserialize CloudEvents to/from JSON +//! * Traits and utilities in [`message`] to implement Protocol Bindings +//! +//! If you're looking for Protocol Binding implementations, look at crates: +//! +//! * [cloudevents-sdk-actix-web](https://docs.rs/cloudevents-sdk-actix-web): Integration with [Actix Web](https://github.com/actix/actix-web) +//! * [cloudevents-sdk-reqwest](https://docs.rs/cloudevents-sdk-reqwest): Integration with [reqwest](https://github.com/seanmonstar/reqwest) +//! * [cloudevents-sdk-rdkafka](https://docs.rs/cloudevents-sdk-rdkafka): Integration with [rdkafka](https://fede1024.github.io/rust-rdkafka) +//! + +<<<<<<< master +#![doc(html_root_url = "https://docs.rs/cloudevents-sdk/0.3.0")] +#![deny(broken_intra_doc_links)] +======= +#![no_std] + +extern crate no_std_compat as std; + +extern crate serde; +extern crate serde_json; +extern crate serde_value; +extern crate snafu; +>>>>>>> no_std #1 + +pub mod event; +pub mod message; + +pub use event::Data; +pub use event::Event; +pub use event::{AttributesReader, AttributesWriter}; +pub use event::{EventBuilder, EventBuilderV03, EventBuilderV10}; diff --git a/.history/src/lib_20201107132439.rs b/.history/src/lib_20201107132439.rs new file mode 100644 index 00000000..7bc164b4 --- /dev/null +++ b/.history/src/lib_20201107132439.rs @@ -0,0 +1,57 @@ +//! This crate implements the [CloudEvents](https://cloudevents.io/) Spec for Rust. +//! +//! ``` +//! # use std::error::Error; +//! # fn main() -> Result<(), Box> { +//! use cloudevents::{EventBuilder, AttributesReader, EventBuilderV10}; +//! use chrono::{Utc, DateTime}; +//! use url::Url; +//! +//! let event = EventBuilderV10::new() +//! .id("my_event.my_application") +//! .source("http://localhost:8080") +//! .ty("example.demo") +//! .time(Utc::now()) +//! .build()?; +//! +//! println!("CloudEvent Id: {}", event.id()); +//! match event.time() { +//! Some(t) => println!("CloudEvent Time: {}", t), +//! None => println!("CloudEvent Time: None") +//! } +//! # Ok(()) +//! # } +//! ``` +//! +//! This crate includes: +//! +//! * The [`Event`] data structure, to represent CloudEvent (version 1.0 and 0.3) +//! * The [`EventBuilder`] trait and implementations, to create [`Event`] instances +//! * The implementation of [`serde::Serialize`] and [`serde::Deserialize`] for [`Event`] to serialize/deserialize CloudEvents to/from JSON +//! * Traits and utilities in [`message`] to implement Protocol Bindings +//! +//! If you're looking for Protocol Binding implementations, look at crates: +//! +//! * [cloudevents-sdk-actix-web](https://docs.rs/cloudevents-sdk-actix-web): Integration with [Actix Web](https://github.com/actix/actix-web) +//! * [cloudevents-sdk-reqwest](https://docs.rs/cloudevents-sdk-reqwest): Integration with [reqwest](https://github.com/seanmonstar/reqwest) +//! * [cloudevents-sdk-rdkafka](https://docs.rs/cloudevents-sdk-rdkafka): Integration with [rdkafka](https://fede1024.github.io/rust-rdkafka) +//! + +#![doc(html_root_url = "https://docs.rs/cloudevents-sdk/0.3.0")] +#![deny(broken_intra_doc_links)] +#![no_std] + +extern crate no_std_compat as std; + +extern crate serde; +extern crate serde_json; +extern crate serde_value; +extern crate snafu; + +pub mod event; +pub mod message; + +pub use event::Data; +pub use event::Event; +pub use event::{AttributesReader, AttributesWriter}; +pub use event::{EventBuilder, EventBuilderV03, EventBuilderV10}; diff --git a/Cargo.toml b/Cargo.toml index 3c0badb4..d95fd8ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,22 +16,33 @@ categories = ["web-programming", "encoding", "data-structures"] [lib] name = "cloudevents" +[features] +default = ["no_std"] +std = ["no_std_compat_bypass","snafu/std", "snafu/guide", "serde/std", "serde_json/std", "chrono/std", "base64/std", "url"] +no_std = ["no_std_compat_bypass", "serde_no_std", "chrono_no_std", "base64/alloc"] + +no_std_compat_bypass=["no-std-compat/std"] +no_std_compat_layer=["no-std-compat/alloc", "no-std-compat/compat_hash", "no-std-compat/compat_sync"] +chrono_no_std = ["chrono/serde", "chrono/alloc", "chrono/clock"] +serde_no_std = ["serde/derive", "serde/alloc", "serde_json/alloc"] + [dependencies] -serde = { version = "^1.0", features = ["derive"] } -serde_json = "^1.0" -chrono = { version = "^0.4", features = ["serde"] } +base64 = { version = "^0.12", default-features = false } +chrono = { version = "^0.4", default-features = false , features = ["serde", "clock"]} delegate-attr = "^0.2" -base64 = "^0.12" -url = { version = "^2.1", features = ["serde"] } -snafu = "^0.6" -bitflags = "^1.2" +serde = { version = "^1.0", default-features=false } +serde_json = { version = "^1.0", default-features = false, features = ["alloc"] } +snafu = { version = "^0.6", default-features = false } +url = { version = "^2.1", features = ["serde"], optional=true } + +no-std-compat = { version = "^0.4.1", features = ["alloc", "compat_hash", "compat_sync"], optional=true } [target."cfg(not(target_arch = \"wasm32\"))".dependencies] hostname = "^0.3" uuid = { version = "^0.8", features = ["v4"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -web-sys = { version = "^0.3", features = ["Window", "Location"] } +web-sys = { version = "^0.3", default-features = false,features = ["Window", "Location"] } uuid = { version = "^0.8", features = ["v4", "wasm-bindgen"] } [dev-dependencies] diff --git a/src/event/attributes.rs b/src/event/attributes.rs index 120bb9c6..beffab1d 100644 --- a/src/event/attributes.rs +++ b/src/event/attributes.rs @@ -5,6 +5,11 @@ use super::{ use chrono::{DateTime, Utc}; use serde::Serializer; use std::fmt; +use std::prelude::v1::*; + +#[cfg(not(feature = "std"))] +use std::string::String as Url; +#[cfg(feature = "std")] use url::Url; /// Enum representing a borrowed value of a CloudEvent attribute. @@ -252,6 +257,7 @@ impl Attributes { } } +#[cfg(feature = "std")] #[cfg(not(target_arch = "wasm32"))] pub(crate) fn default_hostname() -> Url { Url::parse( @@ -268,6 +274,7 @@ pub(crate) fn default_hostname() -> Url { .unwrap() } +#[cfg(feature = "std")] #[cfg(target_arch = "wasm32")] pub(crate) fn default_hostname() -> Url { use std::str::FromStr; @@ -281,3 +288,8 @@ pub(crate) fn default_hostname() -> Url { ) .unwrap() } + +#[cfg(not(feature = "std"))] +pub(crate) fn default_hostname() -> Url { + String::from("http://localhost") +} diff --git a/src/event/builder.rs b/src/event/builder.rs index 2e5790d6..5d95bed2 100644 --- a/src/event/builder.rs +++ b/src/event/builder.rs @@ -1,4 +1,4 @@ -use super::Event; +use super::{url, DisplayError, Event}; use snafu::Snafu; /// Trait to implement a builder for [`Event`]: @@ -41,7 +41,8 @@ pub enum Error { ))] ParseTimeError { attribute_name: &'static str, - source: chrono::ParseError, + #[snafu(source(from(chrono::ParseError, DisplayError)))] + source: DisplayError, }, #[snafu(display( "Error while setting attribute '{}' with uri/uriref type: {}", @@ -50,6 +51,7 @@ pub enum Error { ))] ParseUrlError { attribute_name: &'static str, - source: url::ParseError, + #[snafu(source(from(url::ParseError, DisplayError)))] + source: DisplayError, }, } diff --git a/src/event/data.rs b/src/event/data.rs index c039ce02..42a100a4 100644 --- a/src/event/data.rs +++ b/src/event/data.rs @@ -2,6 +2,7 @@ use serde_json::Value; use std::convert::TryFrom; use std::fmt; use std::fmt::Formatter; +use std::prelude::v1::*; /// Event [data attribute](https://github.com/cloudevents/spec/blob/master/spec.md#event-data) representation #[derive(PartialEq, Eq, Debug, Clone)] diff --git a/src/event/event.rs b/src/event/event.rs new file mode 100644 index 00000000..8193959b --- /dev/null +++ b/src/event/event.rs @@ -0,0 +1,256 @@ +use super::{ + AttributeValue, Attributes, AttributesReader, AttributesV10, AttributesWriter, Data, + ExtensionValue, SpecVersion, +}; +use chrono::{DateTime, Utc}; +use delegate_attr::delegate; +use std::collections::HashMap; +use std::fmt; +use std::prelude::v1::*; +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`] +/// and write them through [`AttributesWriter`]. +/// It also provides methods to read and write the [event data](https://github.com/cloudevents/spec/blob/master/spec.md#event-data). +/// +/// You can build events using [`super::EventBuilder`] +/// ``` +/// use cloudevents::*; +/// use std::convert::TryInto; +/// +/// # use std::error::Error; +/// # fn main() -> Result<(), Box> { +/// // Create an event using the Default trait +/// let mut e = Event::default(); +/// e.set_data( +/// "application/json", +/// serde_json::json!({"hello": "world"}) +/// ); +/// +/// // Print the event id +/// println!("Event id: {}", e.id()); +/// +/// // Get the event data +/// let data: Option = e.data().cloned(); +/// match data { +/// Some(d) => println!("{}", d), +/// None => println!("No event data") +/// } +/// # Ok(()) +/// # } +/// ``` +#[derive(PartialEq, Eq, Debug, Clone)] +pub struct Event { + pub(crate) attributes: Attributes, + pub(crate) data: Option, + pub(crate) extensions: HashMap, +} + +#[delegate(self.attributes)] +impl AttributesReader for Event { + fn id(&self) -> &str; + fn source(&self) -> &Url; + fn specversion(&self) -> SpecVersion; + fn ty(&self) -> &str; + fn datacontenttype(&self) -> Option<&str>; + fn dataschema(&self) -> Option<&Url>; + fn subject(&self) -> Option<&str>; + fn time(&self) -> Option<&DateTime>; +} + +#[delegate(self.attributes)] +impl AttributesWriter for Event { + fn set_id(&mut self, id: 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>) -> Option; + fn set_time(&mut self, time: Option>>) -> Option>; + fn set_datacontenttype(&mut self, datacontenttype: Option>) + -> Option; + fn set_dataschema(&mut self, dataschema: Option>) -> Option; +} + +impl Default for Event { + fn default() -> Self { + Event { + attributes: Attributes::V10(AttributesV10::default()), + data: None, + extensions: HashMap::default(), + } + } +} + +impl fmt::Display for Event { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "CloudEvent:\n")?; + self.iter() + .map(|(name, val)| write!(f, " {}: '{}'\n", name, val)) + .collect::()?; + match self.data() { + Some(data) => write!(f, " {}", data)?, + None => write!(f, " No data")?, + } + write!(f, "\n") + } +} + +impl Event { + /// Returns an [`Iterator`] for all the available [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes) and extensions. + /// Same as chaining [`Event::iter_attributes()`] and [`Event::iter_extensions()`] + pub fn iter(&self) -> impl Iterator { + self.iter_attributes() + .chain(self.extensions.iter().map(|(k, v)| (k.as_str(), v.into()))) + } + + /// Returns an [`Iterator`] for all the available [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes), excluding extensions. + /// This iterator does not contain the `data` field. + pub fn iter_attributes(&self) -> impl Iterator { + self.attributes.iter() + } + + /// Get all the [extensions](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) + pub fn iter_extensions(&self) -> impl Iterator { + self.extensions.iter().map(|(k, v)| (k.as_str(), v)) + } + + /// Get `data` from this `Event` + pub fn data(&self) -> Option<&Data> { + self.data.as_ref() + } + + /// Take (`datacontenttype`, `dataschema`, `data`) from this event, leaving these fields empty + /// + /// ``` + /// use cloudevents::Event; + /// use serde_json::json; + /// use std::convert::Into; + /// + /// let mut e = Event::default(); + /// e.set_data("application/json", json!({})); + /// + /// let (datacontenttype, dataschema, data) = e.take_data(); + /// ``` + pub fn take_data(&mut self) -> (Option, Option, Option) { + ( + self.attributes.set_datacontenttype(None as Option), + self.attributes.set_dataschema(None as Option), + self.data.take(), + ) + } + + /// Set `data` into this `Event` with the specified `datacontenttype`. + /// Returns the previous value of `datacontenttype` and `data`. + /// + /// ``` + /// use cloudevents::Event; + /// use serde_json::json; + /// use std::convert::Into; + /// + /// let mut e = Event::default(); + /// let (old_datacontenttype, old_data) = e.set_data("application/json", json!({})); + /// ``` + pub fn set_data( + &mut self, + datacontenttype: impl Into, + data: impl Into, + ) -> (Option, Option) { + ( + self.attributes.set_datacontenttype(Some(datacontenttype)), + std::mem::replace(&mut self.data, Some(data.into())), + ) + } + + /// Set `data` into this `Event`, without checking if there is a `datacontenttype`. + /// Returns the previous value of `data`. + /// + /// ``` + /// use cloudevents::Event; + /// use serde_json::json; + /// use std::convert::Into; + /// + /// let mut e = Event::default(); + /// let old_data = e.set_data_unchecked(json!({})); + /// ``` + pub fn set_data_unchecked(&mut self, data: impl Into) -> Option { + std::mem::replace(&mut self.data, Some(data.into())) + } + + /// Get the [extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) named `extension_name` + pub fn extension(&self, extension_name: &str) -> Option<&ExtensionValue> { + self.extensions.get(extension_name) + } + + /// Set the [extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) named `extension_name` with `extension_value` + pub fn set_extension<'name, 'event: 'name>( + &'event mut self, + extension_name: &'name str, + extension_value: impl Into, + ) { + self.extensions + .insert(extension_name.to_owned(), extension_value.into()); + } + + /// Remove the [extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) named `extension_name` + pub fn remove_extension<'name, 'event: 'name>( + &'event mut self, + extension_name: &'name str, + ) -> Option { + self.extensions.remove(extension_name) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn take_data() { + let mut e = Event::default(); + e.set_data( + "application/json", + serde_json::json!({ + "hello": "world" + }), + ); + + let (datacontenttype, dataschema, data) = e.take_data(); + + assert!(datacontenttype.is_some()); + assert!(dataschema.is_none()); + assert!(data.is_some()); + + assert!(e.data().is_none()); + assert!(e.dataschema().is_none()); + assert!(e.datacontenttype().is_none()); + } + + #[test] + fn set_id() { + let mut e = Event::default(); + e.set_id("001"); + + assert_eq!(e.set_id("002"), String::from("001")); + assert_eq!(e.id(), "002") + } + + #[test] + fn iter() { + let mut e = Event::default(); + e.set_extension("aaa", "bbb"); + e.set_data( + "application/json", + serde_json::json!({ + "hello": "world" + }), + ); + + let mut v: HashMap<&str, AttributeValue> = e.iter().collect(); + + assert_eq!( + v.remove("specversion"), + Some(AttributeValue::SpecVersion(SpecVersion::V10)) + ); + assert_eq!(v.remove("aaa"), Some(AttributeValue::String("bbb"))) + } +} diff --git a/src/event/extensions.rs b/src/event/extensions.rs index d2670e8a..f3e4ec1e 100644 --- a/src/event/extensions.rs +++ b/src/event/extensions.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize, Serializer}; use std::convert::From; use std::fmt; +use std::prelude::v1::*; #[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] diff --git a/src/event/format.rs b/src/event/format.rs index 5eed6f82..30188840 100644 --- a/src/event/format.rs +++ b/src/event/format.rs @@ -7,6 +7,7 @@ use serde::de::{Error, IntoDeserializer}; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use serde_json::{Map, Value}; use std::collections::HashMap; +use std::prelude::v1::*; macro_rules! parse_field { ($value:expr, $target_type:ty, $error:ty) => { diff --git a/src/event/message.rs b/src/event/message.rs index a65351db..f47da92e 100644 --- a/src/event/message.rs +++ b/src/event/message.rs @@ -7,6 +7,7 @@ use crate::message::{ StructuredSerializer, }; use crate::{EventBuilder, EventBuilderV03, EventBuilderV10}; +use std::prelude::v1::*; impl StructuredDeserializer for Event { fn deserialize_structured>(self, visitor: V) -> Result { diff --git a/src/event/mod.rs b/src/event/mod.rs index 2d88b7ce..84522169 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -41,9 +41,45 @@ pub(crate) use v10::EventFormatSerializer as EventFormatSerializerV10; use chrono::{DateTime, Utc}; use delegate_attr::delegate; use std::collections::HashMap; -use std::fmt; +use std::prelude::v1::*; + +#[cfg(feature = "std")] use url::Url; +#[cfg(not(feature = "std"))] +use String as Url; + +pub trait UrlExtend { + fn parse(&self) -> Result; +} + +impl UrlExtend for Url { + fn parse(&self) -> Result { + Ok(self.to_string()) + } +} + +pub mod url { + use super::{fmt, String}; + #[derive(Debug, Clone)] + pub enum ParseError { + Error(String), + } + + impl snafu::Error for ParseError {} + + impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let ParseError::Error(v) = self { + Ok(()) + } else { + Err(fmt::Error {}) + } + } + } +} + +use core::fmt::{self, Debug, Display}; /// Data structure that represents a [CloudEvent](https://github.com/cloudevents/spec/blob/master/spec.md). /// It provides methods to get the attributes through [`AttributesReader`] /// and write them through [`AttributesWriter`]. @@ -235,6 +271,31 @@ impl Event { } } +// Facilitates compatibility with snafu::Error for external objects + +#[derive(PartialEq, Eq, Clone)] +pub struct DisplayError(pub T); + +impl Debug for DisplayError +where + T: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Display for DisplayError +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl snafu::Error for DisplayError where T: Display + Debug {} + #[cfg(test)] mod tests { use super::*; diff --git a/src/event/spec_version.rs b/src/event/spec_version.rs index 9cdab6db..255ed2f6 100644 --- a/src/event/spec_version.rs +++ b/src/event/spec_version.rs @@ -2,6 +2,7 @@ use super::{v03, v10}; use std::convert::TryFrom; use std::fmt; use std::fmt::Formatter; +use std::prelude::v1::*; pub(crate) const SPEC_VERSIONS: [&str; 2] = ["0.3", "1.0"]; @@ -59,12 +60,12 @@ impl fmt::Display for UnknownSpecVersion { } } -impl std::error::Error for UnknownSpecVersion {} +impl snafu::Error for UnknownSpecVersion {} impl TryFrom<&str> for SpecVersion { type Error = UnknownSpecVersion; - fn try_from(value: &str) -> Result { + fn try_from(value: &str) -> core::result::Result { match value { "0.3" => Ok(SpecVersion::V03), "1.0" => Ok(SpecVersion::V10), diff --git a/src/event/types.rs b/src/event/types.rs index 2ae2f649..b9db6839 100644 --- a/src/event/types.rs +++ b/src/event/types.rs @@ -1,5 +1,11 @@ use chrono::{DateTime, Utc}; -use url::Url; +use std::prelude::v1::*; + +use super::url; +#[cfg(not(feature = "std"))] +use super::{Url, UrlExtend}; +#[cfg(feature = "std")] +use url::{self, ParseError, Url}; /// Trait to define conversion to [`Url`] pub trait TryIntoUrl { @@ -12,12 +18,21 @@ impl TryIntoUrl for Url { } } +#[cfg(not(feature = "std"))] +impl TryIntoUrl for &str { + fn into_url(self) -> Result { + Url::parse(&self.to_string()) + } +} + +#[cfg(feature = "std")] impl TryIntoUrl for &str { fn into_url(self) -> Result { Url::parse(self) } } +#[cfg(feature = "std")] impl TryIntoUrl for String { fn into_url(self) -> Result { self.as_str().into_url() diff --git a/src/event/v03/attributes.rs b/src/event/v03/attributes.rs index cbdb7a23..37b189ae 100644 --- a/src/event/v03/attributes.rs +++ b/src/event/v03/attributes.rs @@ -3,9 +3,14 @@ use crate::event::AttributesV10; use crate::event::{AttributesReader, AttributesWriter, SpecVersion}; use crate::message::{BinarySerializer, MessageAttributeValue}; use chrono::{DateTime, Utc}; -use url::Url; +use std::prelude::v1::*; use uuid::Uuid; +#[cfg(not(feature = "std"))] +use super::super::Url; +#[cfg(feature = "std")] +use url::Url; + pub(crate) const ATTRIBUTE_NAMES: [&str; 8] = [ "specversion", "id", @@ -161,7 +166,7 @@ impl Default for Attributes { datacontenttype: None, schemaurl: None, subject: None, - time: Some(Utc::now()), + time: Some(chrono::offset::Utc::now()), } } } @@ -221,8 +226,10 @@ impl crate::event::message::AttributesDeserializer for super::Attributes { #[cfg(test)] mod tests { use super::*; + use crate::event::UrlExtend; use chrono::NaiveDateTime; + #[cfg(feature = "std")] #[test] fn iterator_test_v03() { let a = Attributes { @@ -258,4 +265,41 @@ mod tests { ); assert_eq!(("time", AttributeValue::Time(&time)), b.next().unwrap()); } + + #[cfg(not(feature = "std"))] + #[test] + fn iterator_test_v03() { + let a = Attributes { + id: String::from("1"), + ty: String::from("someType"), + source: Url::parse(&"https://example.net".to_string()).unwrap(), + datacontenttype: None, + schemaurl: None, + subject: None, + time: Some(DateTime::::from_utc( + NaiveDateTime::from_timestamp(61, 0), + Utc, + )), + }; + let b = &mut a.into_iter(); + let time = DateTime::::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); + + assert_eq!( + ("specversion", AttributeValue::SpecVersion(SpecVersion::V03)), + b.next().unwrap() + ); + assert_eq!(("id", AttributeValue::String("1")), b.next().unwrap()); + assert_eq!( + ("type", AttributeValue::String("someType")), + b.next().unwrap() + ); + assert_eq!( + ( + "source", + AttributeValue::URIRef(&Url::parse(&"https://example.net".to_string()).unwrap()) + ), + b.next().unwrap() + ); + assert_eq!(("time", AttributeValue::Time(&time)), b.next().unwrap()); + } } diff --git a/src/event/v03/builder.rs b/src/event/v03/builder.rs index d40b4368..2dfbc052 100644 --- a/src/event/v03/builder.rs +++ b/src/event/v03/builder.rs @@ -1,11 +1,17 @@ use super::Attributes as AttributesV03; use crate::event::{ - Attributes, Data, Event, EventBuilderError, ExtensionValue, TryIntoTime, TryIntoUrl, + Attributes, Data, DisplayError, Event, EventBuilderError, ExtensionValue, TryIntoTime, + TryIntoUrl, }; use crate::message::MessageAttributeValue; use chrono::{DateTime, Utc}; use std::collections::HashMap; use std::convert::TryInto; +use std::prelude::v1::*; + +#[cfg(not(feature = "std"))] +use super::super::Url; +#[cfg(feature = "std")] use url::Url; /// Builder to create a CloudEvent V0.3 @@ -35,7 +41,7 @@ impl EventBuilder { Err(e) => { self.error = Some(EventBuilderError::ParseUrlError { attribute_name: "source", - source: e, + source: DisplayError(e), }) } }; @@ -58,7 +64,7 @@ impl EventBuilder { Err(e) => { self.error = Some(EventBuilderError::ParseTimeError { attribute_name: "time", - source: e, + source: DisplayError(e), }) } }; @@ -98,7 +104,7 @@ impl EventBuilder { Err(e) => { self.error = Some(EventBuilderError::ParseUrlError { attribute_name: "schemaurl", - source: e, + source: DisplayError(e), }) } }; diff --git a/src/event/v03/format.rs b/src/event/v03/format.rs index e735e10c..5c58200b 100644 --- a/src/event/v03/format.rs +++ b/src/event/v03/format.rs @@ -7,6 +7,11 @@ use serde::ser::SerializeMap; use serde::{Deserialize, Serializer}; use serde_json::{Map, Value}; use std::collections::HashMap; +use std::prelude::v1::*; + +#[cfg(not(feature = "std"))] +use super::super::{Url, UrlExtend}; +#[cfg(feature = "std")] use url::Url; pub(crate) struct EventFormatDeserializer {} diff --git a/src/event/v10/attributes.rs b/src/event/v10/attributes.rs index 4e604424..a8288bc3 100644 --- a/src/event/v10/attributes.rs +++ b/src/event/v10/attributes.rs @@ -3,9 +3,14 @@ use crate::event::{AttributesReader, AttributesV03, AttributesWriter, SpecVersio use crate::message::{BinarySerializer, MessageAttributeValue}; use chrono::{DateTime, Utc}; use core::fmt::Debug; -use url::Url; +use std::prelude::v1::*; use uuid::Uuid; +#[cfg(not(feature = "std"))] +use crate::event::Url; +#[cfg(feature = "std")] +use url::Url; + pub(crate) const ATTRIBUTE_NAMES: [&str; 8] = [ "specversion", "id", @@ -205,6 +210,20 @@ impl AttributesConverter for Attributes { self } + #[cfg(feature = "std")] + fn into_v03(self) -> AttributesV03 { + AttributesV03 { + id: self.id, + ty: self.ty, + source: self.source, + datacontenttype: self.datacontenttype, + schemaurl: self.dataschema, + subject: self.subject, + time: self.time, + } + } + + #[cfg(not(feature = "std"))] fn into_v03(self) -> AttributesV03 { AttributesV03 { id: self.id, @@ -221,8 +240,10 @@ impl AttributesConverter for Attributes { #[cfg(test)] mod tests { use super::*; + use crate::event::UrlExtend; use chrono::NaiveDateTime; + #[cfg(feature = "std")] #[test] fn iterator_test_v10() { let a = Attributes { @@ -258,4 +279,41 @@ mod tests { ); assert_eq!(("time", AttributeValue::Time(&time)), b.next().unwrap()); } + + #[cfg(not(feature = "std"))] + #[test] + fn iterator_test_v10() { + let a = Attributes { + id: String::from("1"), + ty: String::from("someType"), + source: Url::parse(&"https://example.net".to_string()).unwrap(), + datacontenttype: None, + dataschema: None, + subject: None, + time: Some(DateTime::::from_utc( + NaiveDateTime::from_timestamp(61, 0), + Utc, + )), + }; + let b = &mut a.into_iter(); + let time = DateTime::::from_utc(NaiveDateTime::from_timestamp(61, 0), Utc); + + assert_eq!( + ("specversion", AttributeValue::SpecVersion(SpecVersion::V10)), + b.next().unwrap() + ); + assert_eq!(("id", AttributeValue::String("1")), b.next().unwrap()); + assert_eq!( + ("type", AttributeValue::String("someType")), + b.next().unwrap() + ); + assert_eq!( + ( + "source", + AttributeValue::URIRef(&Url::parse(&"https://example.net".to_string()).unwrap()) + ), + b.next().unwrap() + ); + assert_eq!(("time", AttributeValue::Time(&time)), b.next().unwrap()); + } } diff --git a/src/event/v10/builder.rs b/src/event/v10/builder.rs index 1fceeeeb..5f659f52 100644 --- a/src/event/v10/builder.rs +++ b/src/event/v10/builder.rs @@ -1,11 +1,17 @@ use super::Attributes as AttributesV10; use crate::event::{ - Attributes, Data, Event, EventBuilderError, ExtensionValue, TryIntoTime, TryIntoUrl, + Attributes, Data, DisplayError, Event, EventBuilderError, ExtensionValue, TryIntoTime, + TryIntoUrl, }; use crate::message::MessageAttributeValue; use chrono::{DateTime, Utc}; use std::collections::HashMap; use std::convert::TryInto; +use std::prelude::v1::*; + +#[cfg(not(feature = "std"))] +use super::super::Url; +#[cfg(feature = "std")] use url::Url; /// Builder to create a CloudEvent V1.0 @@ -35,7 +41,7 @@ impl EventBuilder { Err(e) => { self.error = Some(EventBuilderError::ParseUrlError { attribute_name: "source", - source: e, + source: DisplayError(e), }) } }; @@ -58,7 +64,7 @@ impl EventBuilder { Err(e) => { self.error = Some(EventBuilderError::ParseTimeError { attribute_name: "time", - source: e, + source: DisplayError(e), }) } }; @@ -98,7 +104,7 @@ impl EventBuilder { Err(e) => { self.error = Some(EventBuilderError::ParseUrlError { attribute_name: "dataschema", - source: e, + source: DisplayError(e), }) } }; diff --git a/src/event/v10/format.rs b/src/event/v10/format.rs index df624639..865235b0 100644 --- a/src/event/v10/format.rs +++ b/src/event/v10/format.rs @@ -7,6 +7,11 @@ use serde::ser::SerializeMap; use serde::{Deserialize, Serializer}; use serde_json::{Map, Value}; use std::collections::HashMap; +use std::prelude::v1::*; + +#[cfg(not(feature = "std"))] +use super::super::{Url, UrlExtend}; +#[cfg(feature = "std")] use url::Url; pub(crate) struct EventFormatDeserializer {} diff --git a/src/lib.rs b/src/lib.rs index 89c6ca41..555d014d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -39,6 +39,16 @@ #![doc(html_root_url = "https://docs.rs/cloudevents-sdk/0.3.1")] #![deny(broken_intra_doc_links)] +#![no_std] + +#[cfg(not(feature = "std"))] +extern crate no_std_compat as std; +#[cfg(feature = "std")] +extern crate std; + +extern crate serde; +extern crate serde_json; +extern crate snafu; pub mod event; pub mod message; diff --git a/src/message/deserializer.rs b/src/message/deserializer.rs index a1fe19e6..03928853 100644 --- a/src/message/deserializer.rs +++ b/src/message/deserializer.rs @@ -1,4 +1,4 @@ -use super::{BinarySerializer, Encoding, Error, Result, StructuredSerializer}; +use super::{BinarySerializer, Encoding, Result, StructuredSerializer}; use crate::event::{EventBinarySerializer, EventStructuredSerializer}; use crate::Event; @@ -46,7 +46,7 @@ where match self.encoding() { Encoding::BINARY => BinaryDeserializer::into_event(self), Encoding::STRUCTURED => StructuredDeserializer::into_event(self), - _ => Err(Error::WrongEncoding {}), + _ => Err(super::error::Error::WrongEncoding {}), } } diff --git a/src/message/error.rs b/src/message/error.rs index 900ae584..7267d9a4 100644 --- a/src/message/error.rs +++ b/src/message/error.rs @@ -1,4 +1,33 @@ +use core::fmt::{self, Debug, Display}; use snafu::Snafu; +use std::prelude::v1::*; + +#[cfg(feature = "std")] +use url; +#[cfg(not(feature = "std"))] +use String as url; + +pub struct DisplayError(pub T); + +impl Debug for DisplayError +where + T: Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl Display for DisplayError +where + T: Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl snafu::Error for DisplayError where T: Display + Debug {} /// Represents an error during serialization/deserialization process #[derive(Debug, Snafu)] @@ -19,21 +48,45 @@ pub enum Error { }, #[snafu(display("Error while parsing a time string: {}", source))] #[snafu(context(false))] - ParseTimeError { source: chrono::ParseError }, + ParseTimeError { + #[snafu(source(from(chrono::ParseError, DisplayError)))] + source: DisplayError, + }, + #[snafu(display("Error while parsing a url: {}", source))] #[snafu(context(false))] - ParseUrlError { source: url::ParseError }, + ParseUrlError { + #[cfg(not(feature = "std"))] + #[snafu(source(from(String, DisplayError)))] + source: DisplayError, + + #[cfg(feature = "std")] + #[snafu(source(from(url::ParseError, DisplayError)))] + source: DisplayError, + }, + #[snafu(display("Error while decoding base64: {}", source))] #[snafu(context(false))] - Base64DecodingError { source: base64::DecodeError }, + Base64DecodingError { + #[snafu(source(from(base64::DecodeError, DisplayError)))] + source: DisplayError, + }, #[snafu(display("Error while serializing/deserializing to json: {}", source))] #[snafu(context(false))] - SerdeJsonError { source: serde_json::Error }, + SerdeJsonError { + #[snafu(source(from(serde_json::Error, DisplayError)))] + source: DisplayError, + }, + + #[cfg(feature = "std")] #[snafu(display("IO Error: {}", source))] #[snafu(context(false))] IOError { source: std::io::Error }, + + #[cfg(feature = "std")] #[snafu(display("Other error: {}", source))] Other { + //#[snafu(source(from(Box, DisplayError)))] source: Box, }, } diff --git a/src/message/hello_no_std_io.rs b/src/message/hello_no_std_io.rs new file mode 100644 index 00000000..baf4f492 --- /dev/null +++ b/src/message/hello_no_std_io.rs @@ -0,0 +1,214 @@ +//! https://docs.rs/not-io/0.1.0-alpha/not_io/ +//! Provides `Read` and `Write` alternatives on `no_std` while being compatible with the full +//! traits from `std` when allowed. +//! +//! ## Motivation +//! +//! The file parser ecosystem of Rust is more or less split across crates that use `no_std` and +//! crates that do not, as well as between crates using `alloc` and no-alloc (and the largely +//! overlapping zero-copy) crates. This has several reasons: +//! +//! * The `std::io::Read` and `std::io::Write` traits require an allocator due to their internal +//! implementation and were not written to be OS independent. +//! * Before `1.36` it was not possible to depend on `alloc` without `std`. +//! * The lack of specialization makes it hard to be both generic over implementors of the standard +//! traits while still allowing use when those traits are not available. This is in particular +//! also since several types (e.g. `&[u8]`) implement those traits but would obviously be useful +//! as byte sources and sinks even when they are unavailable. +//! +//! ## Usage guide +//! +//! This crate assumes you have a structure declared roughly as follows: +//! +//! ```rust +//! # struct SomeItem; +//! # use std::io::Read; +//! +//! struct Decoder { +//! reader: T, +//! } +//! +//! impl Decoder { +//! fn next(&mut self) -> Result { +//! let mut buffer = vec![]; +//! self.reader.read_to_end(&mut buffer)?; +//! # unimplemented!() +//! } +//! } +//! ``` +//! +//! There is only one necessary change, be sure to keep the `std` feature enabled for now. This +//! should not break any code except if you relied on the precise type `T` in which case you will +//! need to use a few derefs and/or `into_inner`. +//! +//! ``` +//! use not_io::AllowStd; +//! # use std::io::Read; +//! +//! struct Decoder { +//! reader: AllowStd, +//! } +//! +//! # struct SomeItem; +//! # impl Decoder { +//! # fn next(&mut self) -> Result { +//! # let mut buffer = vec![]; +//! # self.reader.0.read_to_end(&mut buffer)?; +//! # unimplemented!() +//! # } +//! # } +//! ``` +//! +//! And finally you can add to your crate a new default feature which enables the `std`/`alloc` +//! feature of this crate, and conditionally active your existing interfaces only when that feature +//! is active. Then add a few new impls that can be used even when the feature is inactive. +//! +//! ``` +//! use not_io::AllowStd; +//! # struct SomeItem; +//! +//! struct Decoder { +//! reader: AllowStd, +//! } +//! +//! /// The interface which lets the caller select which feature to turn on. +//! impl Decoder +//! where +//! AllowStd: not_io::Read +//! { +//! fn no_std_next(&mut self) -> Result { +//! # unimplemented!() +//! } +//! } +//! +//! /// An interface for pure no_std use with caller provide no_std reader. +//! impl Decoder +//! where +//! T: not_io::Read +//! { +//! fn not_io_next(&mut self) -> Result { +//! let reader = &mut self.reader.0; +//! # unimplemented!() +//! } +//! } +//! ``` +//! +#![cfg_attr(not(feature = "std"), no_std)] + +#[cfg(all(feature = "alloc", not(feature = "std")))] +extern crate alloc; + +pub type Result = core::result::Result; + +#[derive(Debug)] +pub struct IoError { + _private: (), +} + +impl core::fmt::Display for IoError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!(f, "()") + } +} + +pub trait Read { + fn read(&mut self, buf: &mut [u8]) -> Result; +} + +pub trait Write { + fn write(&mut self, buf: &[u8]) -> Result; +} + +/// A simple new type wrapper holding a potential reader or writer. +/// +/// This type allows the library to satisfy the compatibility across different features without +/// having to resort to specialization. Simply put, this struct implements `Read` and `Write`: +/// +/// * for all types that implement the respective trait from `std` if the `std` feature is active. +/// * on a concrete subset of those types if the `alloc` feature but not the `std` feature has been +/// turned on. +/// * only for types from `core` when neither feature is turned on. +/// +/// Note that without this type we couldn't safely introduce a conditionally active, generic impl +/// of our own traits. The reason is that features must only activate SemVer compatible changes. +/// These two sets of impls are not SemVer compatible due to the uncovered generic `T`. In +/// particular in the first case you'd be allowed to implement the trait for your own type that +/// also implements `std::io::Read` while in the second this is an impl conflict. +/// +/// * `impl Read for &'_ [u8]` +/// * `impl Read for T where std::io::Read` +/// +/// By adding our own private struct as a layer of indirection, you are no longer allowed to make +/// such changes: +/// +/// * `impl Read for AllowStd<&'_ [u8]>` +/// * `impl Read for AllowStd where T: std::io::Read` +/// +/// This still means there is one impl which will never be added. Instead, the impls for +/// core/standard types are provided separately and individually. +/// +/// * `impl Read for AllowStd where T: crate::Read` +pub struct AllowStd(pub T); + +#[cfg(not(feature = "alloc"))] +mod impls_on_neither {} + +#[cfg(feature = "alloc")] +mod impls_on_alloc {} + +#[cfg(feature = "std")] +mod impls_on_std { + use super::{AllowStd, Error, Result}; + use std::io::{self, IoSlice, IoSliceMut}; + + impl super::Read for AllowStd { + fn read(&mut self, buf: &mut [u8]) -> Result { + io::Read::read(&mut self.0, buf).map_err(Error::from) + } + } + + impl io::Read for AllowStd { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } + fn read_vectored(&mut self, bufs: &mut [IoSliceMut]) -> io::Result { + self.0.read_vectored(bufs) + } + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + self.0.read_to_end(buf) + } + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + self.0.read_to_string(buf) + } + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + self.0.read_exact(buf) + } + } + + impl super::Write for AllowStd { + fn write(&mut self, buf: &[u8]) -> Result { + io::Write::write(&mut self.0, buf).map_err(Error::from) + } + } + + impl io::Write for AllowStd { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.0.write(buf) + } + fn flush(&mut self) -> io::Result<()> { + self.0.flush() + } + fn write_vectored(&mut self, bufs: &[IoSlice]) -> io::Result { + self.0.write_vectored(bufs) + } + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.0.write_all(buf) + } + } + + impl From for Error { + fn from(_: io::Error) -> Error { + Error { _private: () } + } + } +} diff --git a/src/message/serializer.rs b/src/message/serializer.rs index c7c0cb5b..5bf476df 100644 --- a/src/message/serializer.rs +++ b/src/message/serializer.rs @@ -1,5 +1,6 @@ use super::{MessageAttributeValue, Result}; use crate::event::SpecVersion; +use std::prelude::v1::*; /// Serializer for structured mode messages. pub trait StructuredSerializer { diff --git a/src/message/types.rs b/src/message/types.rs index 7abf2632..5ab664eb 100644 --- a/src/message/types.rs +++ b/src/message/types.rs @@ -2,7 +2,12 @@ use crate::event::ExtensionValue; use chrono::{DateTime, Utc}; use std::convert::TryInto; use std::fmt; +use std::prelude::v1::*; + +#[cfg(feature = "std")] use url::Url; +#[cfg(not(feature = "std"))] +use String as Url; /// Union type representing a [CloudEvent context attribute type](https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system). #[derive(PartialEq, Eq, Debug, Clone)] @@ -32,6 +37,7 @@ impl TryInto> for MessageAttributeValue { impl TryInto for MessageAttributeValue { type Error = super::Error; + #[cfg(feature = "std")] fn try_into(self) -> Result { match self { MessageAttributeValue::Uri(u) => Ok(u), @@ -39,6 +45,14 @@ impl TryInto for MessageAttributeValue { v => Ok(Url::parse(v.to_string().as_ref())?), } } + #[cfg(not(feature = "std"))] + fn try_into(self) -> Result { + match self { + MessageAttributeValue::Uri(u) => Ok(u), + MessageAttributeValue::UriRef(u) => Ok(u), + v => Ok(v.to_string()), + } + } } impl fmt::Display for MessageAttributeValue { diff --git a/tests/attributes_iter.rs b/tests/attributes_iter.rs index d7d8c6e4..5dee89c2 100644 --- a/tests/attributes_iter.rs +++ b/tests/attributes_iter.rs @@ -3,6 +3,7 @@ use cloudevents::event::AttributeValue; use cloudevents::event::SpecVersion; use test_data::*; +#[cfg(feature = "std")] #[test] fn iter_v10_test() { let in_event = v10::full_no_data(); @@ -14,6 +15,7 @@ fn iter_v10_test() { ); } +#[cfg(feature = "std")] #[test] fn iter_v03_test() { let in_event = v03::full_json_data(); diff --git a/tests/builder_v03.rs b/tests/builder_v03.rs index 6524aa9c..838e752e 100644 --- a/tests/builder_v03.rs +++ b/tests/builder_v03.rs @@ -7,8 +7,14 @@ use cloudevents::event::{ }; use cloudevents::EventBuilderV03; use std::convert::TryInto; + +use cloudevents::event::UrlExtend; +#[cfg(feature = "std")] use url::Url; +#[cfg(not(feature = "std"))] +use String as Url; +#[cfg(feature = "std")] #[test] fn build_event() { let id = "aaa"; diff --git a/tests/builder_v10.rs b/tests/builder_v10.rs index 60f8bade..a6f7020d 100644 --- a/tests/builder_v10.rs +++ b/tests/builder_v10.rs @@ -7,8 +7,14 @@ use cloudevents::event::{ }; use cloudevents::EventBuilderV10; use std::convert::TryInto; + +use cloudevents::event::UrlExtend; +#[cfg(feature = "std")] use url::Url; +#[cfg(not(feature = "std"))] +use String as Url; +#[cfg(feature = "std")] #[test] fn build_event() { let id = "aaa"; diff --git a/tests/message.rs b/tests/message.rs index 4bb3763a..b278df39 100644 --- a/tests/message.rs +++ b/tests/message.rs @@ -5,6 +5,7 @@ use cloudevents::{AttributesReader, EventBuilder, EventBuilderV03, EventBuilderV use std::convert::TryInto; use test_data::*; +#[cfg(feature = "std")] #[test] fn message_v03_roundtrip_structured() -> Result<()> { assert_eq!( @@ -14,6 +15,7 @@ fn message_v03_roundtrip_structured() -> Result<()> { Ok(()) } +#[cfg(feature = "std")] #[test] fn message_v03_roundtrip_binary() -> Result<()> { //TODO this code smells because we're missing a proper way in the public APIs @@ -33,6 +35,7 @@ fn message_v03_roundtrip_binary() -> Result<()> { Ok(()) } +#[cfg(feature = "std")] #[test] fn message_v10_roundtrip_structured() -> Result<()> { assert_eq!( @@ -42,6 +45,7 @@ fn message_v10_roundtrip_structured() -> Result<()> { Ok(()) } +#[cfg(feature = "std")] #[test] fn message_v10_roundtrip_binary() -> Result<()> { //TODO this code smells because we're missing a proper way in the public APIs diff --git a/tests/serde_json.rs b/tests/serde_json.rs index 14f01619..d1877865 100644 --- a/tests/serde_json.rs +++ b/tests/serde_json.rs @@ -7,6 +7,7 @@ mod test_data; use test_data::*; /// This test is a parametrized test that uses data from tests/test_data +#[cfg(feature = "std")] #[rstest( in_event, out_json, @@ -53,6 +54,7 @@ fn serialize_should_succeed(in_event: Event, out_json: Value) { } /// This test is a parametrized test that uses data from tests/test_data +#[cfg(feature = "std")] #[rstest( in_json, out_event, diff --git a/tests/test_data/v03.rs b/tests/test_data/v03.rs index 2ff1c665..121904ac 100644 --- a/tests/test_data/v03.rs +++ b/tests/test_data/v03.rs @@ -2,8 +2,13 @@ use super::*; use cloudevents::{Event, EventBuilder, EventBuilderV03}; use serde_json::{json, Value}; +use cloudevents::event::UrlExtend; +#[cfg(feature = "std")] use url::Url; +#[cfg(not(feature = "std"))] +use String as Url; +#[cfg(feature = "std")] pub fn minimal() -> Event { EventBuilderV03::new() .id(id()) @@ -22,6 +27,7 @@ pub fn minimal_json() -> Value { }) } +#[cfg(feature = "std")] pub fn full_no_data() -> Event { let (string_ext_name, string_ext_value) = string_extension(); let (bool_ext_name, bool_ext_value) = bool_extension(); @@ -58,6 +64,7 @@ pub fn full_no_data_json() -> Value { }) } +#[cfg(feature = "std")] pub fn full_json_data() -> Event { let (string_ext_name, string_ext_value) = string_extension(); let (bool_ext_name, bool_ext_value) = bool_extension(); @@ -124,6 +131,7 @@ pub fn full_json_base64_data_json() -> Value { }) } +#[cfg(feature = "std")] pub fn full_xml_string_data() -> Event { let (string_ext_name, string_ext_value) = string_extension(); let (bool_ext_name, bool_ext_value) = bool_extension(); @@ -143,6 +151,7 @@ pub fn full_xml_string_data() -> Event { .unwrap() } +#[cfg(feature = "std")] pub fn full_xml_binary_data() -> Event { let (string_ext_name, string_ext_value) = string_extension(); let (bool_ext_name, bool_ext_value) = bool_extension(); diff --git a/tests/test_data/v10.rs b/tests/test_data/v10.rs index a4faa9af..7a3693eb 100644 --- a/tests/test_data/v10.rs +++ b/tests/test_data/v10.rs @@ -1,8 +1,14 @@ use super::*; use cloudevents::{Event, EventBuilder, EventBuilderV10}; use serde_json::{json, Value}; + +use cloudevents::event::UrlExtend; +#[cfg(feature = "std")] use url::Url; +#[cfg(not(feature = "std"))] +use String as Url; +#[cfg(feature = "std")] pub fn minimal() -> Event { EventBuilderV10::new() .id(id()) @@ -21,6 +27,7 @@ pub fn minimal_json() -> Value { }) } +#[cfg(feature = "std")] pub fn full_no_data() -> Event { let (string_ext_name, string_ext_value) = string_extension(); let (bool_ext_name, bool_ext_value) = bool_extension(); @@ -57,6 +64,7 @@ pub fn full_no_data_json() -> Value { }) } +#[cfg(feature = "std")] pub fn full_json_data() -> Event { let (string_ext_name, string_ext_value) = string_extension(); let (bool_ext_name, bool_ext_value) = bool_extension(); @@ -122,6 +130,7 @@ pub fn full_json_base64_data_json() -> Value { }) } +#[cfg(feature = "std")] pub fn full_xml_string_data() -> Event { let (string_ext_name, string_ext_value) = string_extension(); let (bool_ext_name, bool_ext_value) = bool_extension(); @@ -141,6 +150,7 @@ pub fn full_xml_string_data() -> Event { .unwrap() } +#[cfg(feature = "std")] pub fn full_xml_binary_data() -> Event { let (string_ext_name, string_ext_value) = string_extension(); let (bool_ext_name, bool_ext_value) = bool_extension(); diff --git a/tests/version_conversion.rs b/tests/version_conversion.rs index bbbb29bb..e6782d09 100644 --- a/tests/version_conversion.rs +++ b/tests/version_conversion.rs @@ -3,6 +3,7 @@ use cloudevents::event::{EventBuilderV03, EventBuilderV10}; use cloudevents::EventBuilder; use test_data::*; +#[cfg(feature = "std")] #[test] fn v10_to_v03() { let in_event = v10::full_json_data(); @@ -10,6 +11,7 @@ fn v10_to_v03() { assert_eq!(v03::full_json_data(), out_event) } +#[cfg(feature = "std")] #[test] fn v03_to_v10() { let in_event = v03::full_json_data();