cloudevents/event/v10/
attributes.rs

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