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#[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}