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