cloudevents/event/v03/
attributes.rs

1use crate::event::attributes::{default_hostname, AttributeValue, AttributesConverter};
2use crate::event::{AttributesReader, AttributesV10, AttributesWriter, SpecVersion, UriReference};
3use crate::message::{BinarySerializer, MessageAttributeValue};
4use chrono::{DateTime, Utc};
5use url::Url;
6use uuid::Uuid;
7
8pub(crate) const ATTRIBUTE_NAMES: [&str; 8] = [
9    "specversion",
10    "id",
11    "type",
12    "source",
13    "datacontenttype",
14    "schemaurl",
15    "subject",
16    "time",
17];
18
19/// Data structure representing [CloudEvents V0.3 context attributes](https://github.com/cloudevents/spec/blob/v0.3/spec.md#context-attributes)
20#[derive(PartialEq, Eq, Debug, Clone)]
21pub struct Attributes {
22    pub(crate) id: String,
23    pub(crate) ty: String,
24    pub(crate) source: UriReference,
25    pub(crate) datacontenttype: Option<String>,
26    pub(crate) schemaurl: Option<Url>,
27    pub(crate) subject: Option<String>,
28    pub(crate) time: Option<DateTime<Utc>>,
29}
30
31impl<'a> IntoIterator for &'a Attributes {
32    type Item = (&'a str, AttributeValue<'a>);
33    type IntoIter = AttributesIntoIterator<'a>;
34
35    fn into_iter(self) -> Self::IntoIter {
36        AttributesIntoIterator {
37            attributes: self,
38            index: 0,
39        }
40    }
41}
42
43#[derive(PartialEq, Debug, Clone, Copy)]
44pub struct AttributesIntoIterator<'a> {
45    pub(crate) attributes: &'a Attributes,
46    pub(crate) index: usize,
47}
48
49impl<'a> Iterator for AttributesIntoIterator<'a> {
50    type Item = (&'a str, AttributeValue<'a>);
51    fn next(&mut self) -> Option<Self::Item> {
52        let result = match self.index {
53            0 => Some(("specversion", AttributeValue::SpecVersion(SpecVersion::V03))),
54            1 => Some(("id", AttributeValue::String(&self.attributes.id))),
55            2 => Some(("type", AttributeValue::String(&self.attributes.ty))),
56            3 => Some(("source", AttributeValue::URIRef(&self.attributes.source))),
57            4 => self
58                .attributes
59                .datacontenttype
60                .as_ref()
61                .map(|v| ("datacontenttype", AttributeValue::String(v))),
62            5 => self
63                .attributes
64                .schemaurl
65                .as_ref()
66                .map(|v| ("schemaurl", AttributeValue::URI(v))),
67            6 => self
68                .attributes
69                .subject
70                .as_ref()
71                .map(|v| ("subject", AttributeValue::String(v))),
72            7 => self
73                .attributes
74                .time
75                .as_ref()
76                .map(|v| ("time", AttributeValue::Time(v))),
77            _ => return None,
78        };
79        self.index += 1;
80        if result.is_none() {
81            return self.next();
82        }
83        result
84    }
85}
86
87impl AttributesReader for Attributes {
88    fn id(&self) -> &str {
89        &self.id
90    }
91
92    fn source(&self) -> &UriReference {
93        &self.source
94    }
95
96    fn specversion(&self) -> SpecVersion {
97        SpecVersion::V03
98    }
99
100    fn ty(&self) -> &str {
101        &self.ty
102    }
103
104    fn datacontenttype(&self) -> Option<&str> {
105        self.datacontenttype.as_deref()
106    }
107
108    fn dataschema(&self) -> Option<&Url> {
109        self.schemaurl.as_ref()
110    }
111
112    fn subject(&self) -> Option<&str> {
113        self.subject.as_deref()
114    }
115
116    fn time(&self) -> Option<&DateTime<Utc>> {
117        self.time.as_ref()
118    }
119}
120
121impl AttributesWriter for Attributes {
122    fn set_id(&mut self, id: impl Into<String>) -> String {
123        std::mem::replace(&mut self.id, id.into())
124    }
125
126    fn set_source(&mut self, source: impl Into<UriReference>) -> UriReference {
127        std::mem::replace(&mut self.source, source.into())
128    }
129
130    fn set_type(&mut self, ty: impl Into<String>) -> String {
131        std::mem::replace(&mut self.ty, ty.into())
132    }
133
134    fn set_subject(&mut self, subject: Option<impl Into<String>>) -> Option<String> {
135        std::mem::replace(&mut self.subject, subject.map(Into::into))
136    }
137
138    fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) -> Option<DateTime<Utc>> {
139        std::mem::replace(&mut self.time, time.map(Into::into))
140    }
141
142    fn set_datacontenttype(
143        &mut self,
144        datacontenttype: Option<impl Into<String>>,
145    ) -> Option<String> {
146        std::mem::replace(&mut self.datacontenttype, datacontenttype.map(Into::into))
147    }
148
149    fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) -> Option<Url> {
150        std::mem::replace(&mut self.schemaurl, dataschema.map(Into::into))
151    }
152}
153
154impl Default for Attributes {
155    fn default() -> Self {
156        Attributes {
157            id: Uuid::new_v4().to_string(),
158            ty: "type".to_string(),
159            source: default_hostname().to_string(),
160            datacontenttype: None,
161            schemaurl: None,
162            subject: None,
163            time: Some(Utc::now()),
164        }
165    }
166}
167
168impl AttributesConverter for Attributes {
169    fn into_v03(self) -> Self {
170        self
171    }
172
173    fn into_v10(self) -> AttributesV10 {
174        AttributesV10 {
175            id: self.id,
176            ty: self.ty,
177            source: self.source,
178            datacontenttype: self.datacontenttype,
179            dataschema: self.schemaurl,
180            subject: self.subject,
181            time: self.time,
182        }
183    }
184}
185
186impl crate::event::message::AttributesDeserializer for super::Attributes {
187    fn deserialize_attributes<R: Sized, V: BinarySerializer<R>>(
188        self,
189        mut visitor: V,
190    ) -> crate::message::Result<V> {
191        visitor = visitor.set_attribute("id", MessageAttributeValue::String(self.id))?;
192        visitor = visitor.set_attribute("type", MessageAttributeValue::String(self.ty))?;
193        visitor = visitor.set_attribute("source", MessageAttributeValue::UriRef(self.source))?;
194        if self.datacontenttype.is_some() {
195            visitor = visitor.set_attribute(
196                "datacontenttype",
197                MessageAttributeValue::String(self.datacontenttype.unwrap()),
198            )?;
199        }
200        if self.schemaurl.is_some() {
201            visitor = visitor.set_attribute(
202                "schemaurl",
203                MessageAttributeValue::Uri(self.schemaurl.unwrap()),
204            )?;
205        }
206        if self.subject.is_some() {
207            visitor = visitor.set_attribute(
208                "subject",
209                MessageAttributeValue::String(self.subject.unwrap()),
210            )?;
211        }
212        if self.time.is_some() {
213            visitor = visitor
214                .set_attribute("time", MessageAttributeValue::DateTime(self.time.unwrap()))?;
215        }
216        Ok(visitor)
217    }
218}
219
220#[cfg(test)]
221mod tests {
222    use super::*;
223    use crate::test::fixtures;
224    use chrono::DateTime;
225
226    #[test]
227    fn iter_v03_test() {
228        let in_event = fixtures::v03::full_json_data();
229        let mut iter_v03 = in_event.iter_attributes();
230
231        assert_eq!(
232            ("specversion", AttributeValue::SpecVersion(SpecVersion::V03)),
233            iter_v03.next().unwrap()
234        );
235    }
236
237    #[test]
238    fn iterator_test_v03() {
239        let a = Attributes {
240            id: String::from("1"),
241            ty: String::from("someType"),
242            source: "https://example.net".into(),
243            datacontenttype: None,
244            schemaurl: None,
245            subject: None,
246            time: DateTime::from_timestamp(61, 0),
247        };
248        let b = &mut a.into_iter();
249        let time = DateTime::from_timestamp(61, 0).unwrap();
250
251        assert_eq!(
252            ("specversion", AttributeValue::SpecVersion(SpecVersion::V03)),
253            b.next().unwrap()
254        );
255        assert_eq!(("id", AttributeValue::String("1")), b.next().unwrap());
256        assert_eq!(
257            ("type", AttributeValue::String("someType")),
258            b.next().unwrap()
259        );
260        assert_eq!(
261            (
262                "source",
263                AttributeValue::URIRef(&"https://example.net".to_string())
264            ),
265            b.next().unwrap()
266        );
267        assert_eq!(("time", AttributeValue::Time(&time)), b.next().unwrap());
268    }
269}