cloudevents/event/
mod.rs

1//! Provides [`Event`] data structure, [`EventBuilder`] and other facilities to work with [`Event`].
2
3mod attributes;
4mod builder;
5mod data;
6mod extensions;
7#[macro_use]
8mod format;
9mod message;
10mod spec_version;
11mod types;
12
13pub use attributes::Attributes;
14pub use attributes::{AttributeValue, AttributesReader, AttributesWriter};
15pub use builder::Error as EventBuilderError;
16pub use builder::EventBuilder;
17pub use data::Data;
18pub use extensions::ExtensionValue;
19pub(crate) use message::EventBinarySerializer;
20pub(crate) use message::EventStructuredSerializer;
21pub use spec_version::SpecVersion;
22pub use spec_version::UnknownSpecVersion;
23pub use types::{TryIntoTime, TryIntoUrl, UriReference};
24
25mod v03;
26
27pub use v03::Attributes as AttributesV03;
28pub(crate) use v03::AttributesIntoIterator as AttributesIntoIteratorV03;
29pub use v03::EventBuilder as EventBuilderV03;
30pub(crate) use v03::EventFormatDeserializer as EventFormatDeserializerV03;
31pub(crate) use v03::EventFormatSerializer as EventFormatSerializerV03;
32
33mod v10;
34
35pub use v10::Attributes as AttributesV10;
36pub(crate) use v10::AttributesIntoIterator as AttributesIntoIteratorV10;
37pub use v10::EventBuilder as EventBuilderV10;
38pub(crate) use v10::EventFormatDeserializer as EventFormatDeserializerV10;
39pub(crate) use v10::EventFormatSerializer as EventFormatSerializerV10;
40
41use chrono::{DateTime, Utc};
42use delegate_attr::delegate;
43use std::collections::HashMap;
44use std::fmt;
45use url::Url;
46
47/// Data structure that represents a [CloudEvent](https://github.com/cloudevents/spec/blob/master/spec.md).
48/// It provides methods to get the attributes through [`AttributesReader`]
49/// and write them through [`AttributesWriter`].
50/// It also provides methods to read and write the [event data](https://github.com/cloudevents/spec/blob/master/spec.md#event-data).
51///
52/// You can build events using [`super::EventBuilder`]
53/// ```
54/// use cloudevents::*;
55/// use std::convert::TryInto;
56///
57/// # use std::error::Error;
58/// # fn main() -> Result<(), Box<dyn Error>> {
59/// // Create an event using the Default trait
60/// let mut e = Event::default();
61/// e.set_data(
62///     "application/json",
63///     serde_json::json!({"hello": "world"})
64/// );
65///
66/// // Print the event id
67/// println!("Event id: {}", e.id());
68///
69/// // Get the event data
70/// let data: Option<Data> = e.data().cloned();
71/// match data {
72///     Some(d) => println!("{}", d),
73///     None => println!("No event data")
74/// }
75/// # Ok(())
76/// # }
77/// ```
78#[derive(PartialEq, Eq, Debug, Clone)]
79pub struct Event {
80    pub(crate) attributes: Attributes,
81    pub(crate) data: Option<Data>,
82    pub(crate) extensions: HashMap<String, ExtensionValue>,
83}
84
85#[delegate(self.attributes)]
86impl AttributesReader for Event {
87    fn id(&self) -> &str {}
88    fn source(&self) -> &UriReference {}
89    fn specversion(&self) -> SpecVersion {}
90    fn ty(&self) -> &str {}
91    fn datacontenttype(&self) -> Option<&str> {}
92    fn dataschema(&self) -> Option<&Url> {}
93    fn subject(&self) -> Option<&str> {}
94    fn time(&self) -> Option<&DateTime<Utc>> {}
95}
96
97#[delegate(self.attributes)]
98impl AttributesWriter for Event {
99    fn set_id(&mut self, id: impl Into<String>) -> String {}
100    fn set_source(&mut self, source: impl Into<UriReference>) -> UriReference {}
101    fn set_type(&mut self, ty: impl Into<String>) -> String {}
102    fn set_subject(&mut self, subject: Option<impl Into<String>>) -> Option<String> {}
103    fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) -> Option<DateTime<Utc>> {}
104    fn set_datacontenttype(
105        &mut self,
106        datacontenttype: Option<impl Into<String>>,
107    ) -> Option<String> {
108    }
109    fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) -> Option<Url> {}
110}
111
112impl Default for Event {
113    fn default() -> Self {
114        Event {
115            attributes: Attributes::V10(AttributesV10::default()),
116            data: None,
117            extensions: HashMap::default(),
118        }
119    }
120}
121
122impl fmt::Display for Event {
123    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124        writeln!(f, "CloudEvent:")?;
125        self.iter()
126            .try_for_each(|(name, val)| writeln!(f, "  {}: '{}'", name, val))?;
127        match self.data() {
128            Some(data) => write!(f, "  {}", data)?,
129            None => write!(f, "  No data")?,
130        }
131        writeln!(f)
132    }
133}
134
135impl Event {
136    /// Returns an [`Iterator`] for all the available [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes) and extensions.
137    /// Same as chaining [`Event::iter_attributes()`] and [`Event::iter_extensions()`]
138    pub fn iter(&self) -> impl Iterator<Item = (&str, AttributeValue)> {
139        self.iter_attributes()
140            .chain(self.extensions.iter().map(|(k, v)| (k.as_str(), v.into())))
141    }
142
143    /// Returns an [`Iterator`] for all the available [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes), excluding extensions.
144    /// This iterator does not contain the `data` field.
145    pub fn iter_attributes(&self) -> impl Iterator<Item = (&str, AttributeValue)> {
146        self.attributes.iter()
147    }
148
149    /// Get all the [extensions](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes)
150    pub fn iter_extensions(&self) -> impl Iterator<Item = (&str, &ExtensionValue)> {
151        self.extensions.iter().map(|(k, v)| (k.as_str(), v))
152    }
153
154    /// Get `data` from this `Event`
155    pub fn data(&self) -> Option<&Data> {
156        self.data.as_ref()
157    }
158
159    /// Take (`datacontenttype`, `dataschema`, `data`) from this event, leaving these fields empty
160    ///
161    /// ```
162    /// use cloudevents::Event;
163    /// use serde_json::json;
164    /// use std::convert::Into;
165    ///
166    /// let mut e = Event::default();
167    /// e.set_data("application/json", json!({}));
168    ///
169    /// let (datacontenttype, dataschema, data) = e.take_data();
170    /// ```
171    pub fn take_data(&mut self) -> (Option<String>, Option<Url>, Option<Data>) {
172        (
173            self.attributes.set_datacontenttype(None as Option<String>),
174            self.attributes.set_dataschema(None as Option<Url>),
175            self.data.take(),
176        )
177    }
178
179    /// Set `data` into this `Event` with the specified `datacontenttype`.
180    /// Returns the previous value of `datacontenttype` and `data`.
181    ///
182    /// ```
183    /// use cloudevents::Event;
184    /// use serde_json::json;
185    /// use std::convert::Into;
186    ///
187    /// let mut e = Event::default();
188    /// let (old_datacontenttype, old_data) = e.set_data("application/json", json!({}));
189    /// ```
190    pub fn set_data(
191        &mut self,
192        datacontenttype: impl Into<String>,
193        data: impl Into<Data>,
194    ) -> (Option<String>, Option<Data>) {
195        (
196            self.attributes.set_datacontenttype(Some(datacontenttype)),
197            std::mem::replace(&mut self.data, Some(data.into())),
198        )
199    }
200
201    /// Set `data` into this `Event`, without checking if there is a `datacontenttype`.
202    /// Returns the previous value of `data`.
203    ///
204    /// ```
205    /// use cloudevents::Event;
206    /// use serde_json::json;
207    /// use std::convert::Into;
208    ///
209    /// let mut e = Event::default();
210    /// let old_data = e.set_data_unchecked(json!({}));
211    /// ```
212    pub fn set_data_unchecked(&mut self, data: impl Into<Data>) -> Option<Data> {
213        std::mem::replace(&mut self.data, Some(data.into()))
214    }
215
216    /// Get the [extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) named `extension_name`
217    pub fn extension(&self, extension_name: &str) -> Option<&ExtensionValue> {
218        self.extensions.get(extension_name)
219    }
220
221    /// Set the [extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) named `extension_name` with `extension_value`
222    pub fn set_extension<'name, 'event: 'name>(
223        &'event mut self,
224        extension_name: &'name str,
225        extension_value: impl Into<ExtensionValue>,
226    ) {
227        self.extensions
228            .insert(extension_name.to_owned(), extension_value.into());
229    }
230
231    /// Remove the [extension](https://github.com/cloudevents/spec/blob/master/spec.md#extension-context-attributes) named `extension_name`
232    pub fn remove_extension<'name, 'event: 'name>(
233        &'event mut self,
234        extension_name: &'name str,
235    ) -> Option<ExtensionValue> {
236        self.extensions.remove(extension_name)
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243
244    #[test]
245    fn take_data() {
246        let mut e = Event::default();
247        e.set_data(
248            "application/json",
249            serde_json::json!({
250                "hello": "world"
251            }),
252        );
253
254        let (datacontenttype, dataschema, data) = e.take_data();
255
256        assert!(datacontenttype.is_some());
257        assert!(dataschema.is_none());
258        assert!(data.is_some());
259
260        assert!(e.data().is_none());
261        assert!(e.dataschema().is_none());
262        assert!(e.datacontenttype().is_none());
263    }
264
265    #[test]
266    fn set_id() {
267        let mut e = Event::default();
268        e.set_id("001");
269
270        assert_eq!(e.set_id("002"), String::from("001"));
271        assert_eq!(e.id(), "002")
272    }
273
274    #[test]
275    fn iter() {
276        let mut e = Event::default();
277        e.set_extension("aaa", "bbb");
278        e.set_data(
279            "application/json",
280            serde_json::json!({
281                "hello": "world"
282            }),
283        );
284
285        let mut v: HashMap<&str, AttributeValue> = e.iter().collect();
286
287        assert_eq!(
288            v.remove("specversion"),
289            Some(AttributeValue::SpecVersion(SpecVersion::V10))
290        );
291        assert_eq!(v.remove("aaa"), Some(AttributeValue::String("bbb")))
292    }
293}