From 27238b3f2f0b0508ce27833ff118b162408494f3 Mon Sep 17 00:00:00 2001 From: slinkydeveloper Date: Fri, 10 Apr 2020 19:05:17 +0200 Subject: [PATCH] Playing with macros. Generated Attributes struct, attributes names vector and Default trait impl with a macro_rules Signed-off-by: Francesco Guardiani --- README.md | 4 +- src/event/attributes.rs | 73 +++++++++++++++++++++++++++++++++++++ src/event/mod.rs | 1 + src/event/v03/attributes.rs | 43 ++++++++++------------ src/event/v10/attributes.rs | 58 +++++++++++------------------ 5 files changed, 117 insertions(+), 62 deletions(-) diff --git a/README.md b/README.md index 996af60..e3fca18 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ Work in progress SDK for [CloudEvents](https://github.com/cloudevents/spec) | | [v0.3](https://github.com/cloudevents/spec/tree/v0.3) | [v1.0](https://github.com/cloudevents/spec/tree/v1.0) | | :---------------------------: | :----------------------------------------------------------------------------: | :---------------------------------------------------------------------------------: | -| CloudEvents Core | :x: | :heavy_check_mark: | +| CloudEvents Core | :heavy_check_mark: | :heavy_check_mark: | | AMQP Protocol Binding | :x: | :x: | | AVRO Event Format | :x: | :x: | | HTTP Protocol Binding | :x: | :x: | -| JSON Event Format | :x: | :heavy_check_mark: | +| JSON Event Format | :heavy_check_mark: | :heavy_check_mark: | | Kafka Protocol Binding | :x: | :x: | | MQTT Protocol Binding | :x: | :x: | | NATS Protocol Binding | :x: | :x: | diff --git a/src/event/attributes.rs b/src/event/attributes.rs index 5cfa5be..3b822e7 100644 --- a/src/event/attributes.rs +++ b/src/event/attributes.rs @@ -1,6 +1,79 @@ use super::{AttributesV03, AttributesV10, SpecVersion}; use chrono::{DateTime, Utc}; +macro_rules! attributes_def { + // struct attributes expansion + (@struct_gen $struct_name:ident {$(,)*} -> { $($out:tt)* }) => { + #[derive(PartialEq, Debug, Clone)] + pub struct $struct_name { + $($out)* + } + }; + (@struct_gen $struct_name:ident {$element_name:ident $(as $_element_lit:literal)?: $element_ty:ty $({$($opts:tt)*})?, $($tail:tt)*} -> {}) => { + attributes_def!(@struct_gen $struct_name { $($tail)* } -> {pub(crate) $element_name: $element_ty}); + }; + (@struct_gen $struct_name:ident {$element_name:ident $(as $_element_lit:literal)?: $element_ty:ty $({$($opts:tt)*})?, $($tail:tt)*} -> {$($out:tt)*}) => { + attributes_def!(@struct_gen $struct_name { $($tail)* } -> {$($out)*, pub(crate) $element_name: $element_ty}); + }; + + // count attributes + (@count_attrs ) => {0usize}; + (@count_attrs $_element_name:ident $(as $_element_lit:literal)?: $element_ty:ty $({$($opts:tt)*})?, $($tail:tt)*) => {1usize + attributes_def!{@count_attrs $($tail)* }}; + + // names expansion + (@attributes_gen {} -> {$($out:expr)*} ) => { + [$($out),*] + }; + (@attributes_gen { $element_name:ident: $_element_ty:ty $({$($_opts:tt)*})?, $($tail:tt)* } -> {$($out:tt)*}) => { + attributes_def!(@attributes_gen {$($tail)*} -> {$($out)* stringify!($element_name)}) + }; + (@attributes_gen { $_element_name:ident as $element_lit:literal: $_element_ty:ty $({$($_opts:tt)*})? , $($tail:tt)* } -> {$($out:tt)*}) => { + attributes_def!(@attributes_gen {$($tail)*} -> {$($out)* $element_lit}) + }; + + // attribute names expansion + (@attribute_names_gen $attr_vec_name:ident { $($attrs:tt)* }) => { + pub const $attr_vec_name: [&'static str; attributes_def!(@count_attrs $($attrs)*)] = attributes_def!(@attributes_gen { $($attrs)* } -> {}); + }; + + // default trait implementation expansion + (@default_gen $struct_name:ident {$(,)*} -> { $($out:tt)* }) => { + impl Default for $struct_name { + fn default() -> Self { + $struct_name { + $($out)* + } + } + } + }; + (@default_gen $struct_name:ident { + $element_name:ident $(as $_element_lit:literal)?: $_element_ty:ty, $($tail:tt)* + } -> { $($out:tt)* } ) => { + attributes_def!(@default_gen $struct_name { $($tail)* } -> {$($out)* $element_name: attributes_def!(@default_gen_walk_opts {}), }); + }; + (@default_gen $struct_name:ident { + $element_name:ident $(as $_element_lit:literal)?: $_element_ty:ty {$($opts:tt)*}, $($tail:tt)* + } -> { $($out:tt)* } ) => { + attributes_def!(@default_gen $struct_name { $($tail)* } -> {$($out)* $element_name: attributes_def!(@default_gen_walk_opts {$($opts)*}), }); + }; + (@default_gen_walk_opts {default: $default_expr:expr, $($tail:tt)*}) => { + $default_expr + }; + (@default_gen_walk_opts {$_opt_key:ident: $_opt_val:ident, $($tail:tt)*}) => { + attributes_def!(@default_gen_walk_opts {$($tail)*}) + }; + (@default_gen_walk_opts {}) => { + Default::default() + }; + + // Real macro input + ($struct_name:ident, $attr_names:ident, { $($tt:tt)* }) => { + attributes_def!(@attribute_names_gen $attr_names { $($tt)* }); + attributes_def!(@struct_gen $struct_name { $($tt)* } -> {}); + attributes_def!(@default_gen $struct_name { $($tt)* } -> {}); + }; +} + /// 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). diff --git a/src/event/mod.rs b/src/event/mod.rs index 3af1494..2db7bc8 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -1,3 +1,4 @@ +#[macro_use] mod attributes; mod builder; mod data; diff --git a/src/event/v03/attributes.rs b/src/event/v03/attributes.rs index 1e75629..9accc31 100644 --- a/src/event/v03/attributes.rs +++ b/src/event/v03/attributes.rs @@ -5,16 +5,25 @@ use chrono::{DateTime, Utc}; use hostname::get_hostname; use uuid::Uuid; -#[derive(PartialEq, Debug, Clone)] -pub struct Attributes { - pub(crate) id: String, - pub(crate) ty: String, - pub(crate) source: String, - pub(crate) datacontenttype: Option, - pub(crate) schemaurl: Option, - pub(crate) subject: Option, - pub(crate) time: Option>, -} +attributes_def!( + Attributes, + ATTRIBUTES_NAMES, + { + id: String { + default: Uuid::new_v4().to_string(), + }, + ty as "type": String { + default: "rust.generated".to_string(), + }, + source: String { + default: get_hostname().unwrap_or("http://localhost/".to_string()), + }, + datacontenttype: Option, + schemaurl: Option, + subject: Option, + time: Option>, + } +); impl AttributesReader for Attributes { fn get_id(&self) -> &str { @@ -91,20 +100,6 @@ impl DataAttributesWriter for Attributes { } } -impl Default for Attributes { - fn default() -> Self { - Attributes { - id: Uuid::new_v4().to_string(), - ty: "type".to_string(), - source: get_hostname().unwrap_or("http://localhost/".to_string()), - datacontenttype: None, - schemaurl: None, - subject: None, - time: None, - } - } -} - impl AttributesConverter for Attributes { fn into_v03(self) -> Self { self diff --git a/src/event/v10/attributes.rs b/src/event/v10/attributes.rs index cd3d92b..4c73db7 100644 --- a/src/event/v10/attributes.rs +++ b/src/event/v10/attributes.rs @@ -4,16 +4,25 @@ use chrono::{DateTime, Utc}; use hostname::get_hostname; use uuid::Uuid; -#[derive(PartialEq, Debug, Clone)] -pub struct Attributes { - pub(crate) id: String, - pub(crate) ty: String, - pub(crate) source: String, - pub(crate) datacontenttype: Option, - pub(crate) dataschema: Option, - pub(crate) subject: Option, - pub(crate) time: Option>, -} +attributes_def!( + Attributes, + ATTRIBUTES_NAMES, + { + id: String { + default: Uuid::new_v4().to_string(), + }, + ty as "type": String { + default: "rust.generated".to_string(), + }, + source: String { + default: get_hostname().unwrap_or("http://localhost/".to_string()), + }, + datacontenttype: Option, + dataschema: Option, + subject: Option, + time: Option>, + } +); impl AttributesReader for Attributes { fn get_id(&self) -> &str { @@ -33,24 +42,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, - } + self.dataschema.as_deref() } 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> { @@ -90,20 +90,6 @@ impl DataAttributesWriter for Attributes { } } -impl Default for Attributes { - fn default() -> Self { - Attributes { - id: Uuid::new_v4().to_string(), - ty: "type".to_string(), - source: get_hostname().unwrap_or("http://localhost/".to_string()), - datacontenttype: None, - dataschema: None, - subject: None, - time: None, - } - } -} - impl AttributesConverter for Attributes { fn into_v10(self) -> Self { self