diff --git a/Cargo.toml b/Cargo.toml index e9cccd0..0cdaa03 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ repository = "https://github.com/cloudevents/sdk-rust" exclude = [ ".github/*" ] +categories = ["web-programming", "encoding", "data-structures"] [lib] name = "cloudevents" @@ -36,6 +37,7 @@ uuid = { version = "^0.8", features = ["v4", "wasm-bindgen"] } [dev-dependencies] rstest = "0.6" claim = "0.3.1" +version-sync = "^0.9" [workspace] members = [ diff --git a/README.md b/README.md index 4a77b31..902a722 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,7 @@ Note: This project is WIP under active development, hence all APIs are considere To get started, add the dependency to `Cargo.toml`: ```toml +[dependencies] cloudevents-sdk = "0.2.0" ``` @@ -43,8 +44,7 @@ let event = EventBuilderV10::new() .id("aaa") .source(Url::parse("http://localhost").unwrap()) .ty("example.demo") - .build() - .unwrap(); + .build()?; ``` Checkout the examples using our integrations to learn how to send and receive events: diff --git a/cloudevents-sdk-actix-web/Cargo.toml b/cloudevents-sdk-actix-web/Cargo.toml index a63ed95..608ba69 100644 --- a/cloudevents-sdk-actix-web/Cargo.toml +++ b/cloudevents-sdk-actix-web/Cargo.toml @@ -8,6 +8,7 @@ description = "CloudEvents official Rust SDK - Actix-Web integration" documentation = "https://docs.rs/cloudevents-sdk-actix-web" repository = "https://github.com/cloudevents/sdk-rust" readme = "README.md" +categories = ["web-programming", "encoding", "web-programming::http-server"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -23,4 +24,5 @@ futures = "^0.3" [dev-dependencies] url = { version = "^2.1", features = ["serde"] } serde_json = "^1.0" -chrono = { version = "^0.4", features = ["serde"] } \ No newline at end of file +chrono = { version = "^0.4", features = ["serde"] } +version-sync = "^0.9" \ No newline at end of file diff --git a/cloudevents-sdk-actix-web/src/lib.rs b/cloudevents-sdk-actix-web/src/lib.rs index 1bf2d72..d54c41f 100644 --- a/cloudevents-sdk-actix-web/src/lib.rs +++ b/cloudevents-sdk-actix-web/src/lib.rs @@ -3,7 +3,7 @@ //! To deserialize an HTTP request as CloudEvent: //! //! ``` -//! use cloudevents_sdk_actix_web::RequestExt; +//! use cloudevents_sdk_actix_web::HttpRequestExt; //! use actix_web::{HttpRequest, web, post}; //! //! #[post("/")] @@ -41,6 +41,9 @@ //! //! Check out the [cloudevents-sdk](https://docs.rs/cloudevents-sdk) docs for more details on how to use [`cloudevents::Event`] +#![doc(html_root_url = "https://docs.rs/cloudevents-sdk-actix-web/0.2.0")] +#![deny(broken_intra_doc_links)] + #[macro_use] mod headers; mod server_request; @@ -48,7 +51,7 @@ mod server_response; pub use server_request::request_to_event; pub use server_request::HttpRequestDeserializer; -pub use server_request::RequestExt; +pub use server_request::HttpRequestExt; pub use server_response::event_to_response; pub use server_response::HttpResponseBuilderExt; pub use server_response::HttpResponseSerializer; diff --git a/cloudevents-sdk-actix-web/src/server_request.rs b/cloudevents-sdk-actix-web/src/server_request.rs index 68a0f1f..20d708b 100644 --- a/cloudevents-sdk-actix-web/src/server_request.rs +++ b/cloudevents-sdk-actix-web/src/server_request.rs @@ -113,8 +113,11 @@ pub async fn request_to_event( } /// Extention Trait for [`HttpRequest`] which acts as a wrapper for the function [`request_to_event()`]. +/// +/// This trait is sealed and cannot be implemented for types outside of this crate. #[async_trait(?Send)] -pub trait RequestExt { +pub trait HttpRequestExt: private::Sealed { + /// Convert this [`HttpRequest`] into an [`Event`]. async fn to_event( &self, mut payload: web::Payload, @@ -122,7 +125,7 @@ pub trait RequestExt { } #[async_trait(?Send)] -impl RequestExt for HttpRequest { +impl HttpRequestExt for HttpRequest { async fn to_event( &self, payload: web::Payload, @@ -131,6 +134,12 @@ impl RequestExt for HttpRequest { } } +mod private { + // Sealing the RequestExt + pub trait Sealed {} + impl Sealed for actix_web::HttpRequest {} +} + #[cfg(test)] mod tests { use super::*; diff --git a/cloudevents-sdk-actix-web/src/server_response.rs b/cloudevents-sdk-actix-web/src/server_response.rs index e125c14..650029e 100644 --- a/cloudevents-sdk-actix-web/src/server_response.rs +++ b/cloudevents-sdk-actix-web/src/server_response.rs @@ -76,9 +76,12 @@ pub async fn event_to_response( .map_err(actix_web::error::ErrorBadRequest) } -/// Extention Trait for [`HttpResponseBuilder`] which acts as a wrapper for the function [`event_to_response()`]. +/// Extension Trait for [`HttpResponseBuilder`] which acts as a wrapper for the function [`event_to_response()`]. +/// +/// This trait is sealed and cannot be implemented for types outside of this crate. #[async_trait(?Send)] -pub trait HttpResponseBuilderExt { +pub trait HttpResponseBuilderExt: private::Sealed { + /// Fill this [`HttpResponseBuilder`] with an [`Event`]. async fn event( self, event: Event, @@ -95,6 +98,12 @@ impl HttpResponseBuilderExt for HttpResponseBuilder { } } +// Sealing the HttpResponseBuilderExt +mod private { + pub trait Sealed {} + impl Sealed for actix_web::dev::HttpResponseBuilder {} +} + #[cfg(test)] mod tests { use super::*; diff --git a/cloudevents-sdk-actix-web/tests/version_number.rs b/cloudevents-sdk-actix-web/tests/version_number.rs new file mode 100644 index 0000000..288592d --- /dev/null +++ b/cloudevents-sdk-actix-web/tests/version_number.rs @@ -0,0 +1,9 @@ +#[test] +fn test_readme_deps() { + version_sync::assert_markdown_deps_updated!("README.md"); +} + +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +} diff --git a/cloudevents-sdk-rdkafka/Cargo.toml b/cloudevents-sdk-rdkafka/Cargo.toml index 9dc02fc..17c5849 100644 --- a/cloudevents-sdk-rdkafka/Cargo.toml +++ b/cloudevents-sdk-rdkafka/Cargo.toml @@ -8,6 +8,7 @@ description = "CloudEvents official Rust SDK - Kafka integration" documentation = "https://docs.rs/cloudevents-sdk-rdkafka" repository = "https://github.com/cloudevents/sdk-rust" readme = "README.md" +categories = ["web-programming", "encoding"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -15,11 +16,12 @@ readme = "README.md" bytes = "^0.5" cloudevents-sdk = { version = "0.2.0", path = ".." } lazy_static = "1.4.0" -rdkafka = { version = "^0.24", features = ["cmake-build"] } - +rdkafka = { version = "^0.24", default-features = false } [dev-dependencies] url = { version = "^2.1" } serde_json = "^1.0" chrono = { version = "^0.4", features = ["serde"] } futures = "0.3.5" +rdkafka = { version = "^0.24" } +version-sync = "^0.9" diff --git a/cloudevents-sdk-rdkafka/src/kafka_consumer_record.rs b/cloudevents-sdk-rdkafka/src/kafka_consumer_record.rs index 4efbe5c..980d6de 100644 --- a/cloudevents-sdk-rdkafka/src/kafka_consumer_record.rs +++ b/cloudevents-sdk-rdkafka/src/kafka_consumer_record.rs @@ -134,8 +134,10 @@ pub fn record_to_event(msg: &impl Message) -> Result { MessageDeserializer::into_event(ConsumerRecordDeserializer::new(msg)?) } -/// Extension Trait for [`Message`] which acts as a wrapper for the function [`record_to_event()`] -pub trait MessageExt { +/// Extension Trait for [`Message`] which acts as a wrapper for the function [`record_to_event()`]. +/// +/// This trait is sealed and cannot be implemented for types outside of this crate. +pub trait MessageExt: private::Sealed { /// Generates [`Event`] from [`BorrowedMessage`]. fn to_event(&self) -> Result; } @@ -152,6 +154,13 @@ impl MessageExt for OwnedMessage { } } +mod private { + // Sealing the MessageExt + pub trait Sealed {} + impl Sealed for rdkafka::message::OwnedMessage {} + impl Sealed for rdkafka::message::BorrowedMessage<'_> {} +} + #[cfg(test)] mod tests { use super::*; diff --git a/cloudevents-sdk-rdkafka/src/kafka_producer_record.rs b/cloudevents-sdk-rdkafka/src/kafka_producer_record.rs index 1a83cd6..e1a420b 100644 --- a/cloudevents-sdk-rdkafka/src/kafka_producer_record.rs +++ b/cloudevents-sdk-rdkafka/src/kafka_producer_record.rs @@ -88,7 +88,9 @@ impl StructuredSerializer for MessageRecord { } /// Extension Trait for [`BaseRecord`] that fills the record with a [`MessageRecord`]. -pub trait BaseRecordExt<'a, K: ToBytes + ?Sized> { +/// +/// This trait is sealed and cannot be implemented for types outside of this crate. +pub trait BaseRecordExt<'a, K: ToBytes + ?Sized>: private::Sealed { /// Fill this [`BaseRecord`] with a [`MessageRecord`]. fn message_record( self, @@ -112,7 +114,9 @@ impl<'a, K: ToBytes + ?Sized> BaseRecordExt<'a, K> for BaseRecord<'a, K, Vec } /// Extension Trait for [`FutureRecord`] that fills the record with a [`MessageRecord`]. -pub trait FutureRecordExt<'a, K: ToBytes + ?Sized> { +/// +/// This trait is sealed and cannot be implemented for types outside of this crate. +pub trait FutureRecordExt<'a, K: ToBytes + ?Sized>: private::Sealed { /// Fill this [`FutureRecord`] with a [`MessageRecord`]. fn message_record(self, message_record: &'a MessageRecord) -> FutureRecord<'a, K, Vec>; } @@ -128,3 +132,16 @@ impl<'a, K: ToBytes + ?Sized> FutureRecordExt<'a, K> for FutureRecord<'a, K, Vec self } } + +mod private { + // Sealing the FutureRecordExt and BaseRecordExt + pub trait Sealed {} + impl Sealed + for rdkafka::producer::FutureRecord<'_, K, V> + { + } + impl Sealed + for rdkafka::producer::BaseRecord<'_, K, V> + { + } +} diff --git a/cloudevents-sdk-rdkafka/src/lib.rs b/cloudevents-sdk-rdkafka/src/lib.rs index 76235fa..f2df202 100644 --- a/cloudevents-sdk-rdkafka/src/lib.rs +++ b/cloudevents-sdk-rdkafka/src/lib.rs @@ -10,9 +10,8 @@ //! use rdkafka::util::Timeout; //! use cloudevents_sdk_rdkafka::{MessageRecord, FutureRecordExt}; //! -//! # async fn produce(producer: &FutureProducer, event: Event) { -//! let message_record = MessageRecord::from_event(event) -//! .expect("error while serializing the event"); +//! # async fn produce(producer: &FutureProducer, event: Event) -> Result<(), Box> { +//! let message_record = MessageRecord::from_event(event)?; //! //! producer.send( //! FutureRecord::to("topic") @@ -20,7 +19,7 @@ //! .message_record(&message_record), //! Timeout::Never //! ).await; -//! +//! # Ok(()) //! # } //! //! ``` @@ -32,22 +31,26 @@ //! use cloudevents_sdk_rdkafka::MessageExt; //! use futures::StreamExt; //! -//! # async fn consume(consumer: StreamConsumer) { +//! # async fn consume(consumer: StreamConsumer) -> Result<(), Box> { //! let mut message_stream = consumer.start(); //! //! while let Some(message) = message_stream.next().await { //! match message { //! Err(e) => println!("Kafka error: {}", e), //! Ok(m) => { -//! let event = m.to_event().expect("error while deserializing record to CloudEvent"); -//! println!("Received Event: {:#?}", event); -//! consumer.commit_message(&m, CommitMode::Async).unwrap(); +//! let event = m.to_event()?; +//! println!("Received Event: {}", event); +//! consumer.commit_message(&m, CommitMode::Async)?; //! } //! }; //! } +//! # Ok(()) //! # } //! ``` +#![doc(html_root_url = "https://docs.rs/cloudevents-sdk-rdkafka/0.2.0")] +#![deny(broken_intra_doc_links)] + #[macro_use] mod headers; mod kafka_consumer_record; diff --git a/cloudevents-sdk-rdkafka/tests/version_number.rs b/cloudevents-sdk-rdkafka/tests/version_number.rs new file mode 100644 index 0000000..288592d --- /dev/null +++ b/cloudevents-sdk-rdkafka/tests/version_number.rs @@ -0,0 +1,9 @@ +#[test] +fn test_readme_deps() { + version_sync::assert_markdown_deps_updated!("README.md"); +} + +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +} diff --git a/cloudevents-sdk-reqwest/Cargo.toml b/cloudevents-sdk-reqwest/Cargo.toml index 50a0fa3..96c4a46 100644 --- a/cloudevents-sdk-reqwest/Cargo.toml +++ b/cloudevents-sdk-reqwest/Cargo.toml @@ -8,6 +8,7 @@ description = "CloudEvents official Rust SDK - Reqwest integration" documentation = "https://docs.rs/cloudevents-sdk-reqwest" repository = "https://github.com/cloudevents/sdk-rust" readme = "README.md" +categories = ["web-programming", "encoding", "web-programming::http-client"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -27,4 +28,5 @@ mockito = "0.25.1" tokio = { version = "^0.2", features = ["full"] } url = { version = "^2.1" } serde_json = "^1.0" -chrono = { version = "^0.4", features = ["serde"] } \ No newline at end of file +chrono = { version = "^0.4", features = ["serde"] } +version-sync = "^0.9" \ No newline at end of file diff --git a/cloudevents-sdk-reqwest/src/client_request.rs b/cloudevents-sdk-reqwest/src/client_request.rs index 1e5d395..edd837a 100644 --- a/cloudevents-sdk-reqwest/src/client_request.rs +++ b/cloudevents-sdk-reqwest/src/client_request.rs @@ -62,13 +62,16 @@ impl StructuredSerializer for RequestSerializer { } } -/// Method to fill a [`RequestBuilder`] with an [`Event`] +/// Method to fill a [`RequestBuilder`] with an [`Event`]. pub fn event_to_request(event: Event, request_builder: RequestBuilder) -> Result { BinaryDeserializer::deserialize_binary(event, RequestSerializer::new(request_builder)) } -/// Extention Trait for [`RequestBuilder`] which acts as a wrapper for the function [`event_to_request()`] -pub trait RequestBuilderExt { +/// Extension Trait for [`RequestBuilder`] which acts as a wrapper for the function [`event_to_request()`]. +/// +/// This trait is sealed and cannot be implemented for types outside of this crate. +pub trait RequestBuilderExt: private::Sealed { + /// Write in this [`RequestBuilder`] the provided [`Event`]. Similar to invoking [`Event`]. fn event(self, event: Event) -> Result; } @@ -78,6 +81,12 @@ impl RequestBuilderExt for RequestBuilder { } } +// Sealing the RequestBuilderExt +mod private { + pub trait Sealed {} + impl Sealed for reqwest::RequestBuilder {} +} + #[cfg(test)] mod tests { use super::*; diff --git a/cloudevents-sdk-reqwest/src/client_response.rs b/cloudevents-sdk-reqwest/src/client_response.rs index 10bb582..fc7d1a7 100644 --- a/cloudevents-sdk-reqwest/src/client_response.rs +++ b/cloudevents-sdk-reqwest/src/client_response.rs @@ -109,8 +109,11 @@ pub async fn response_to_event(res: Response) -> Result { } /// Extension Trait for [`Response`] which acts as a wrapper for the function [`response_to_event()`]. +/// +/// This trait is sealed and cannot be implemented for types outside of this crate. #[async_trait(?Send)] -pub trait ResponseExt { +pub trait ResponseExt: private::Sealed { + /// Convert this [`Response`] to [`Event`]. async fn into_event(self) -> Result; } @@ -121,6 +124,12 @@ impl ResponseExt for Response { } } +// Sealing the ResponseExt +mod private { + pub trait Sealed {} + impl Sealed for reqwest::Response {} +} + #[cfg(test)] mod tests { use super::*; diff --git a/cloudevents-sdk-reqwest/src/lib.rs b/cloudevents-sdk-reqwest/src/lib.rs index 73e98be..f96bb09 100644 --- a/cloudevents-sdk-reqwest/src/lib.rs +++ b/cloudevents-sdk-reqwest/src/lib.rs @@ -5,7 +5,7 @@ //! use cloudevents::{EventBuilderV10, EventBuilder}; //! use serde_json::json; //! -//! # async fn example() { +//! # async fn example() -> Result<(), Box> { //! let client = reqwest::Client::new(); //! //! // Prepare the event to send @@ -14,23 +14,23 @@ //! .ty("example.test") //! .source("http://localhost/") //! .data("application/json", json!({"hello": "world"})) -//! .build() -//! .expect("No error while building the event"); +//! .build()?; //! //! // Send request //! let response = client.post("http://localhost") -//! .event(event_to_send) -//! .expect("Error while serializing the event") -//! .send().await -//! .expect("Error while sending the request"); +//! .event(event_to_send)? +//! .send().await?; //! // Parse response as event //! let received_event = response -//! .into_event().await -//! .expect("Error while deserializing the response"); +//! .into_event().await?; +//! # Ok(()) //! # } //! ``` //! -//! Check out the [cloudevents-sdk](https://docs.rs/cloudevents-sdk) docs for more details on how to use [`cloudevents::Event`] +//! Check out the [cloudevents-sdk](https://docs.rs/cloudevents-sdk) docs for more details on how to use [`cloudevents::Event`]. + +#![doc(html_root_url = "https://docs.rs/cloudevents-sdk-reqwest/0.2.0")] +#![deny(broken_intra_doc_links)] #[macro_use] mod headers; diff --git a/cloudevents-sdk-reqwest/tests/version_number.rs b/cloudevents-sdk-reqwest/tests/version_number.rs new file mode 100644 index 0000000..288592d --- /dev/null +++ b/cloudevents-sdk-reqwest/tests/version_number.rs @@ -0,0 +1,9 @@ +#[test] +fn test_readme_deps() { + version_sync::assert_markdown_deps_updated!("README.md"); +} + +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +} diff --git a/example-projects/actix-web-example/Cargo.toml b/example-projects/actix-web-example/Cargo.toml index 41002a4..4c5225c 100644 --- a/example-projects/actix-web-example/Cargo.toml +++ b/example-projects/actix-web-example/Cargo.toml @@ -7,9 +7,9 @@ edition = "2018" [dependencies] cloudevents-sdk = { path = "../.." } cloudevents-sdk-actix-web = { path = "../../cloudevents-sdk-actix-web" } -actix-web = "2" +actix-web = "^3" actix-rt = "1" -actix-cors = "^0.2.0" +actix-cors = "^0.5" lazy_static = "1.4.0" bytes = "^0.5" futures = "^0.3" diff --git a/example-projects/actix-web-example/src/main.rs b/example-projects/actix-web-example/src/main.rs index 24c58b4..7422fe0 100644 --- a/example-projects/actix-web-example/src/main.rs +++ b/example-projects/actix-web-example/src/main.rs @@ -1,6 +1,6 @@ use actix_web::{get, post, web, App, HttpRequest, HttpResponse, HttpServer}; use cloudevents::{EventBuilder, EventBuilderV10}; -use cloudevents_sdk_actix_web::{HttpResponseBuilderExt, RequestExt}; +use cloudevents_sdk_actix_web::{HttpResponseBuilderExt, HttpRequestExt}; use serde_json::json; #[post("/")] @@ -36,7 +36,7 @@ async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .wrap(actix_web::middleware::Logger::default()) - .wrap(actix_cors::Cors::new().finish()) + .wrap(actix_cors::Cors::permissive()) .service(post_event) .service(get_event) }) diff --git a/example-projects/rdkafka-example/Cargo.toml b/example-projects/rdkafka-example/Cargo.toml index 6972df7..dce2373 100644 --- a/example-projects/rdkafka-example/Cargo.toml +++ b/example-projects/rdkafka-example/Cargo.toml @@ -19,5 +19,7 @@ tokio = { version = "^0.2", features = ["full"] } clap = "2.33.1" [dependencies.rdkafka] -version = "~0.23" -features = ["ssl", "sasl"] \ No newline at end of file +version = "^0.24" +features = ["cmake-build"] + +[workspace] diff --git a/example-projects/rdkafka-example/src/main.rs b/example-projects/rdkafka-example/src/main.rs index 0306171..7e1c138 100644 --- a/example-projects/rdkafka-example/src/main.rs +++ b/example-projects/rdkafka-example/src/main.rs @@ -9,6 +9,7 @@ use rdkafka::config::{ClientConfig, RDKafkaLogLevel}; use rdkafka::consumer::stream_consumer::StreamConsumer; use rdkafka::consumer::{CommitMode, Consumer, DefaultConsumerContext}; use rdkafka::producer::{FutureProducer, FutureRecord}; +use std::time::Duration; // You need a running Kafka cluster to try out this example. // With docker: docker run --rm --net=host -e ADV_HOST=localhost -e SAMPLEDATA=0 lensesio/fast-data-dev @@ -78,7 +79,7 @@ async fn produce(brokers: &str, topic_name: &str) { FutureRecord::to(topic_name) .message_record(&message_record) .key(&format!("Key {}", i)), - 0, + Duration::from_secs(10), ) .await; diff --git a/src/event/attributes.rs b/src/event/attributes.rs index 216e81a..fcf8760 100644 --- a/src/event/attributes.rs +++ b/src/event/attributes.rs @@ -7,7 +7,8 @@ use serde::Serializer; use std::fmt; use url::Url; -/// Value of a CloudEvent attribute +/// Enum representing a borrowed value of a CloudEvent attribute. +/// This represents the types defined in the [CloudEvent spec type system](https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system) #[derive(Debug, PartialEq, Eq)] pub enum AttributeValue<'a> { SpecVersion(SpecVersion), @@ -230,20 +231,20 @@ impl AttributesWriter for Attributes { } impl Attributes { - pub fn into_v10(self) -> Self { + pub(crate) fn into_v10(self) -> Self { match self { Attributes::V03(v03) => Attributes::V10(v03.into_v10()), _ => self, } } - pub fn into_v03(self) -> Self { + pub(crate) fn into_v03(self) -> Self { match self { Attributes::V10(v10) => Attributes::V03(v10.into_v03()), _ => self, } } - pub fn iter(&self) -> impl Iterator { + pub(crate) fn iter(&self) -> impl Iterator { match self { Attributes::V03(a) => AttributesIter::IterV03(a.into_iter()), Attributes::V10(a) => AttributesIter::IterV10(a.into_iter()), diff --git a/src/event/data.rs b/src/event/data.rs index 4d9ae7d..53445b5 100644 --- a/src/event/data.rs +++ b/src/event/data.rs @@ -1,6 +1,6 @@ use serde::export::Formatter; use serde_json::Value; -use std::convert::{Into, TryFrom}; +use std::convert::TryFrom; use std::fmt; /// Event [data attribute](https://github.com/cloudevents/spec/blob/master/spec.md#event-data) representation @@ -14,40 +14,6 @@ pub enum Data { Json(serde_json::Value), } -impl Data { - /// Create a [`Data`] from a [`Into>`]. - /// - /// # Example - /// - /// ``` - /// use cloudevents::event::Data; - /// - /// let value = Data::from_base64(b"dmFsdWU=").unwrap(); - /// assert_eq!(value, Data::Binary(base64::decode("dmFsdWU=").unwrap())); - /// ``` - /// - /// [`AsRef<[u8]>`]: https://doc.rust-lang.org/std/convert/trait.AsRef.html - /// [`Data`]: enum.Data.html - pub fn from_base64(i: I) -> Result - where - I: AsRef<[u8]>, - { - Ok(base64::decode(&i)?.into()) - } - - pub fn from_binary(content_type: Option<&str>, i: I) -> Result - where - I: AsRef<[u8]>, - { - let is_json = is_json_content_type(content_type.unwrap_or("application/json")); - if is_json { - serde_json::from_slice::(i.as_ref()).map(Data::Json) - } else { - Ok(Data::Binary(i.as_ref().to_vec())) - } - } -} - pub(crate) fn is_json_content_type(ct: &str) -> bool { ct.starts_with("application/json") || ct.starts_with("text/json") || ct.ends_with("+json") } diff --git a/src/event/extensions.rs b/src/event/extensions.rs index d467d39..d2670e8 100644 --- a/src/event/extensions.rs +++ b/src/event/extensions.rs @@ -6,11 +6,11 @@ use std::fmt; #[serde(untagged)] /// Represents all the possible [CloudEvents extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) values pub enum ExtensionValue { - /// Represents a [`String`](std::string::String) value. + /// Represents a [`String`] value. String(String), - /// Represents a [`bool`](bool) value. + /// Represents a [`bool`] value. Boolean(bool), - /// Represents an integer [`i64`](i64) value. + /// Represents an integer [`i64`] value. Integer(i64), } diff --git a/src/event/mod.rs b/src/event/mod.rs index d42ba50..b6a70d0 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -1,3 +1,5 @@ +//! Provides [`Event`] data structure, [`EventBuilder`] and other facilities to work with [`Event`]. + mod attributes; mod builder; mod data; diff --git a/src/event/spec_version.rs b/src/event/spec_version.rs index cdaa7e3..7fe3a2b 100644 --- a/src/event/spec_version.rs +++ b/src/event/spec_version.rs @@ -5,14 +5,18 @@ use std::fmt; pub(crate) const SPEC_VERSIONS: [&'static str; 2] = ["0.3", "1.0"]; -/// CloudEvent specification version +/// CloudEvent specification version. #[derive(PartialEq, Eq, Hash, Debug, Clone)] pub enum SpecVersion { + /// CloudEvents v0.3 V03, + /// CloudEvents v1.0 V10, } impl SpecVersion { + /// Returns the string representation of [`SpecVersion`]. + #[inline] pub fn as_str(&self) -> &str { match self { SpecVersion::V03 => "0.3", @@ -28,7 +32,7 @@ impl SpecVersion { SpecVersion::V10 => &v10::ATTRIBUTE_NAMES, } } - /// Get all attribute names for all Spec versions. + /// Get all attribute names for all specification versions. /// Note that the result iterator could contain duplicate entries. pub fn all_attribute_names() -> impl Iterator { vec![SpecVersion::V03, SpecVersion::V10] diff --git a/src/lib.rs b/src/lib.rs index 5ce5283..e149e66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,10 @@ //! 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; +//! use chrono::{Utc, DateTime}; //! use url::Url; //! //! let event = EventBuilderV10::new() @@ -10,27 +12,35 @@ //! .source("http://localhost:8080") //! .ty("example.demo") //! .time(Utc::now()) -//! .build() -//! .unwrap(); +//! .build()?; //! //! println!("CloudEvent Id: {}", event.id()); -//! println!("CloudEvent Time: {}", event.time().unwrap()); +//! 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) //! -extern crate serde; -extern crate serde_json; -extern crate serde_value; -extern crate snafu; +#![doc(html_root_url = "https://docs.rs/cloudevents-sdk/0.2.0")] +#![deny(broken_intra_doc_links)] -/// Provides [`Event`] data structure, [`EventBuilder`] and other facilities to work with [`Event`] pub mod event; -/// Provides facilities to implement Protocol Bindings pub mod message; pub use event::Data; diff --git a/src/message/deserializer.rs b/src/message/deserializer.rs index 7df0821..fee96c8 100644 --- a/src/message/deserializer.rs +++ b/src/message/deserializer.rs @@ -2,46 +2,46 @@ use super::{BinarySerializer, Encoding, Error, Result, StructuredSerializer}; use crate::event::{EventBinarySerializer, EventStructuredSerializer}; use crate::Event; -/// Deserializer trait for a Message that can be encoded as structured mode +/// Deserializer trait for a Message that can be encoded as structured mode. pub trait StructuredDeserializer where Self: Sized, { - /// Deserialize the message to [`StructuredSerializer`] + /// Deserialize the message to [`StructuredSerializer`]. fn deserialize_structured>( self, serializer: V, ) -> Result; - /// Convert this Message to [`Event`] + /// Convert this Message to [`Event`]. fn into_event(self) -> Result { self.deserialize_structured(EventStructuredSerializer {}) } } -/// Deserializer trait for a Message that can be encoded as binary mode +/// Deserializer trait for a Message that can be encoded as binary mode. pub trait BinaryDeserializer where Self: Sized, { - /// Deserialize the message to [`BinarySerializer`] + /// Deserialize the message to [`BinarySerializer`]. fn deserialize_binary>(self, serializer: V) -> Result; - /// Convert this Message to [`Event`] + /// Convert this Message to [`Event`]. fn into_event(self) -> Result { self.deserialize_binary(EventBinarySerializer::new()) } } -/// Deserializer trait for a Message that can be encoded both in structured mode or binary mode +/// Deserializer trait for a Message that can be encoded both in structured mode or binary mode. pub trait MessageDeserializer where Self: StructuredDeserializer + BinaryDeserializer + Sized, { - /// Get this message [`Encoding`] + /// Get this message [`Encoding`]. fn encoding(&self) -> Encoding; - /// Convert this Message to [`Event`] + /// Convert this Message to [`Event`]. fn into_event(self) -> Result { match self.encoding() { Encoding::BINARY => BinaryDeserializer::into_event(self), @@ -50,7 +50,7 @@ where } } - /// Deserialize the message to [`BinarySerializer`] + /// Deserialize the message to [`BinarySerializer`]. fn deserialize_to_binary>(self, serializer: T) -> Result { if self.encoding() == Encoding::BINARY { return self.deserialize_binary(serializer); @@ -59,7 +59,7 @@ where return MessageDeserializer::into_event(self)?.deserialize_binary(serializer); } - /// Deserialize the message to [`StructuredSerializer`] + /// Deserialize the message to [`StructuredSerializer`]. fn deserialize_to_structured>( self, serializer: T, @@ -71,8 +71,8 @@ where return MessageDeserializer::into_event(self)?.deserialize_structured(serializer); } - /// Deserialize the message to a serializer, depending on the message encoding - /// You can use this method to transcode this message directly to another serializer, without going through [`Event`] + /// Deserialize the message to a serializer, depending on the message encoding. + /// You can use this method to transcode this message directly to another serializer, without going through [`Event`]. fn deserialize_to + StructuredSerializer>( self, serializer: T, diff --git a/src/message/encoding.rs b/src/message/encoding.rs index 69a6558..d8fb5de 100644 --- a/src/message/encoding.rs +++ b/src/message/encoding.rs @@ -1,9 +1,12 @@ use std::fmt::Debug; -/// Represents one of the possible [message encodings/modes](https://github.com/cloudevents/spec/blob/v1.0/spec.md#message) +/// Represents one of the possible [message encodings/modes](https://github.com/cloudevents/spec/blob/v1.0/spec.md#message). #[derive(PartialEq, Eq, Debug)] pub enum Encoding { + /// Represents the _structured-mode message_. STRUCTURED, + /// Represents the _binary-mode message_. BINARY, + /// Represents a non-CloudEvent or a malformed CloudEvent that cannot be recognized by the SDK. UNKNOWN, } diff --git a/src/message/mod.rs b/src/message/mod.rs index 46d869d..1466057 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -1,3 +1,7 @@ +//! Provides facilities to implement Protocol Bindings. +//! +//! Note: these APIs should be considered unstable and subject to changes. + mod deserializer; mod encoding; mod error; diff --git a/src/message/serializer.rs b/src/message/serializer.rs index f71171b..c7c0cb5 100644 --- a/src/message/serializer.rs +++ b/src/message/serializer.rs @@ -1,12 +1,12 @@ use super::{MessageAttributeValue, Result}; use crate::event::SpecVersion; -/// Serializer for structured mode messages +/// Serializer for structured mode messages. pub trait StructuredSerializer { fn set_structured_event(self, bytes: Vec) -> Result; } -/// Serializer for binary mode messages +/// Serializer for binary mode messages. pub trait BinarySerializer where Self: Sized, diff --git a/src/message/types.rs b/src/message/types.rs index 3d12fd9..7abf263 100644 --- a/src/message/types.rs +++ b/src/message/types.rs @@ -1,11 +1,10 @@ use crate::event::ExtensionValue; use chrono::{DateTime, Utc}; -use serde::export::Formatter; use std::convert::TryInto; use std::fmt; use url::Url; -/// Union type representing a [CloudEvent context attribute type](https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system) +/// 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)] pub enum MessageAttributeValue { Boolean(bool), diff --git a/tests/version_number.rs b/tests/version_number.rs new file mode 100644 index 0000000..288592d --- /dev/null +++ b/tests/version_number.rs @@ -0,0 +1,9 @@ +#[test] +fn test_readme_deps() { + version_sync::assert_markdown_deps_updated!("README.md"); +} + +#[test] +fn test_html_root_url() { + version_sync::assert_html_root_url_updated!("src/lib.rs"); +}