cloudevents/event/
attributes.rs

1use super::{
2    AttributesIntoIteratorV03, AttributesIntoIteratorV10, AttributesV03, AttributesV10,
3    ExtensionValue, SpecVersion, UriReference,
4};
5use base64::prelude::*;
6use chrono::{DateTime, Utc};
7use serde::Serializer;
8use std::fmt;
9use url::Url;
10
11/// Enum representing a borrowed value of a CloudEvent attribute.
12/// This represents the types defined in the [CloudEvent spec type system](https://github.com/cloudevents/spec/blob/v1.0/spec.md#type-system)
13#[derive(Debug, PartialEq, Eq)]
14pub enum AttributeValue<'a> {
15    Boolean(&'a bool),
16    Integer(&'a i64),
17    String(&'a str),
18    Binary(&'a [u8]),
19    URI(&'a Url),
20    URIRef(&'a UriReference),
21    Time(&'a DateTime<Utc>),
22    SpecVersion(SpecVersion),
23}
24
25impl<'a> From<&'a ExtensionValue> for AttributeValue<'a> {
26    fn from(ev: &'a ExtensionValue) -> Self {
27        match ev {
28            ExtensionValue::String(s) => AttributeValue::String(s),
29            ExtensionValue::Boolean(b) => AttributeValue::Boolean(b),
30            ExtensionValue::Integer(i) => AttributeValue::Integer(i),
31        }
32    }
33}
34
35impl fmt::Display for AttributeValue<'_> {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        match self {
38            AttributeValue::Boolean(b) => f.serialize_bool(**b),
39            AttributeValue::Integer(i) => f.serialize_i64(**i),
40            AttributeValue::String(s) => f.write_str(s),
41            AttributeValue::Binary(b) => f.write_str(&BASE64_STANDARD.encode(b)),
42            AttributeValue::URI(s) => f.write_str(s.as_str()),
43            AttributeValue::URIRef(s) => f.write_str(s.as_str()),
44            AttributeValue::Time(s) => f.write_str(&s.to_rfc3339()),
45            AttributeValue::SpecVersion(s) => s.fmt(f),
46        }
47    }
48}
49
50/// Trait to get [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes).
51pub trait AttributesReader {
52    /// Get the [id](https://github.com/cloudevents/spec/blob/master/spec.md#id).
53    fn id(&self) -> &str;
54    /// Get the [source](https://github.com/cloudevents/spec/blob/master/spec.md#source-1).
55    fn source(&self) -> &UriReference;
56    /// Get the [specversion](https://github.com/cloudevents/spec/blob/master/spec.md#specversion).
57    fn specversion(&self) -> SpecVersion;
58    /// Get the [type](https://github.com/cloudevents/spec/blob/master/spec.md#type).
59    fn ty(&self) -> &str;
60    /// Get the [datacontenttype](https://github.com/cloudevents/spec/blob/master/spec.md#datacontenttype).
61    fn datacontenttype(&self) -> Option<&str>;
62    /// Get the [dataschema](https://github.com/cloudevents/spec/blob/master/spec.md#dataschema).
63    fn dataschema(&self) -> Option<&Url>;
64    /// Get the [subject](https://github.com/cloudevents/spec/blob/master/spec.md#subject).
65    fn subject(&self) -> Option<&str>;
66    /// Get the [time](https://github.com/cloudevents/spec/blob/master/spec.md#time).
67    fn time(&self) -> Option<&DateTime<Utc>>;
68}
69
70/// Trait to set [CloudEvents Context attributes](https://github.com/cloudevents/spec/blob/master/spec.md#context-attributes).
71pub trait AttributesWriter {
72    /// Set the [id](https://github.com/cloudevents/spec/blob/master/spec.md#id).
73    /// Returns the previous value.
74    fn set_id(&mut self, id: impl Into<String>) -> String;
75    /// Set the [source](https://github.com/cloudevents/spec/blob/master/spec.md#source-1).
76    /// Returns the previous value.
77    fn set_source(&mut self, source: impl Into<UriReference>) -> UriReference;
78    /// Set the [type](https://github.com/cloudevents/spec/blob/master/spec.md#type).
79    /// Returns the previous value.
80    fn set_type(&mut self, ty: impl Into<String>) -> String;
81    /// Set the [subject](https://github.com/cloudevents/spec/blob/master/spec.md#subject).
82    /// Returns the previous value.
83    fn set_subject(&mut self, subject: Option<impl Into<String>>) -> Option<String>;
84    /// Set the [time](https://github.com/cloudevents/spec/blob/master/spec.md#time).
85    /// Returns the previous value.
86    fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) -> Option<DateTime<Utc>>;
87    /// Set the [datacontenttype](https://github.com/cloudevents/spec/blob/master/spec.md#datacontenttype).
88    /// Returns the previous value.
89    fn set_datacontenttype(&mut self, datacontenttype: Option<impl Into<String>>)
90        -> Option<String>;
91    /// Set the [dataschema](https://github.com/cloudevents/spec/blob/master/spec.md#dataschema).
92    /// Returns the previous value.
93    fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) -> Option<Url>;
94}
95
96pub(crate) trait AttributesConverter {
97    fn into_v03(self) -> AttributesV03;
98    fn into_v10(self) -> AttributesV10;
99}
100
101#[derive(PartialEq, Debug, Clone, Copy)]
102pub(crate) enum AttributesIter<'a> {
103    IterV03(AttributesIntoIteratorV03<'a>),
104    IterV10(AttributesIntoIteratorV10<'a>),
105}
106
107impl<'a> Iterator for AttributesIter<'a> {
108    type Item = (&'a str, AttributeValue<'a>);
109    fn next(&mut self) -> Option<Self::Item> {
110        match self {
111            AttributesIter::IterV03(a) => a.next(),
112            AttributesIter::IterV10(a) => a.next(),
113        }
114    }
115}
116
117/// Union type representing one of the possible context attributes structs
118#[derive(PartialEq, Eq, Debug, Clone)]
119pub enum Attributes {
120    V03(AttributesV03),
121    V10(AttributesV10),
122}
123
124impl AttributesReader for Attributes {
125    fn id(&self) -> &str {
126        match self {
127            Attributes::V03(a) => a.id(),
128            Attributes::V10(a) => a.id(),
129        }
130    }
131
132    fn source(&self) -> &UriReference {
133        match self {
134            Attributes::V03(a) => a.source(),
135            Attributes::V10(a) => a.source(),
136        }
137    }
138
139    fn specversion(&self) -> SpecVersion {
140        match self {
141            Attributes::V03(a) => a.specversion(),
142            Attributes::V10(a) => a.specversion(),
143        }
144    }
145
146    fn ty(&self) -> &str {
147        match self {
148            Attributes::V03(a) => a.ty(),
149            Attributes::V10(a) => a.ty(),
150        }
151    }
152
153    fn datacontenttype(&self) -> Option<&str> {
154        match self {
155            Attributes::V03(a) => a.datacontenttype(),
156            Attributes::V10(a) => a.datacontenttype(),
157        }
158    }
159
160    fn dataschema(&self) -> Option<&Url> {
161        match self {
162            Attributes::V03(a) => a.dataschema(),
163            Attributes::V10(a) => a.dataschema(),
164        }
165    }
166
167    fn subject(&self) -> Option<&str> {
168        match self {
169            Attributes::V03(a) => a.subject(),
170            Attributes::V10(a) => a.subject(),
171        }
172    }
173
174    fn time(&self) -> Option<&DateTime<Utc>> {
175        match self {
176            Attributes::V03(a) => a.time(),
177            Attributes::V10(a) => a.time(),
178        }
179    }
180}
181
182impl AttributesWriter for Attributes {
183    fn set_id(&mut self, id: impl Into<String>) -> String {
184        match self {
185            Attributes::V03(a) => a.set_id(id),
186            Attributes::V10(a) => a.set_id(id),
187        }
188    }
189
190    fn set_source(&mut self, source: impl Into<UriReference>) -> UriReference {
191        match self {
192            Attributes::V03(a) => a.set_source(source),
193            Attributes::V10(a) => a.set_source(source),
194        }
195    }
196
197    fn set_type(&mut self, ty: impl Into<String>) -> String {
198        match self {
199            Attributes::V03(a) => a.set_type(ty),
200            Attributes::V10(a) => a.set_type(ty),
201        }
202    }
203
204    fn set_subject(&mut self, subject: Option<impl Into<String>>) -> Option<String> {
205        match self {
206            Attributes::V03(a) => a.set_subject(subject),
207            Attributes::V10(a) => a.set_subject(subject),
208        }
209    }
210
211    fn set_time(&mut self, time: Option<impl Into<DateTime<Utc>>>) -> Option<DateTime<Utc>> {
212        match self {
213            Attributes::V03(a) => a.set_time(time),
214            Attributes::V10(a) => a.set_time(time),
215        }
216    }
217
218    fn set_datacontenttype(
219        &mut self,
220        datacontenttype: Option<impl Into<String>>,
221    ) -> Option<String> {
222        match self {
223            Attributes::V03(a) => a.set_datacontenttype(datacontenttype),
224            Attributes::V10(a) => a.set_datacontenttype(datacontenttype),
225        }
226    }
227
228    fn set_dataschema(&mut self, dataschema: Option<impl Into<Url>>) -> Option<Url> {
229        match self {
230            Attributes::V03(a) => a.set_dataschema(dataschema),
231            Attributes::V10(a) => a.set_dataschema(dataschema),
232        }
233    }
234}
235
236impl Attributes {
237    pub(crate) fn into_v10(self) -> Self {
238        match self {
239            Attributes::V03(v03) => Attributes::V10(v03.into_v10()),
240            _ => self,
241        }
242    }
243    pub(crate) fn into_v03(self) -> Self {
244        match self {
245            Attributes::V10(v10) => Attributes::V03(v10.into_v03()),
246            _ => self,
247        }
248    }
249
250    pub(crate) fn iter(&self) -> impl Iterator<Item = (&str, AttributeValue)> {
251        match self {
252            Attributes::V03(a) => AttributesIter::IterV03(a.into_iter()),
253            Attributes::V10(a) => AttributesIter::IterV10(a.into_iter()),
254        }
255    }
256}
257
258#[cfg(not(target_arch = "wasm32"))]
259pub(crate) fn default_hostname() -> Url {
260    Url::parse(
261        format!(
262            "http://{}",
263            hostname::get()
264                .ok()
265                .and_then(|s| s.into_string().ok())
266                .unwrap_or_else(|| "localhost".to_string())
267        )
268        .as_ref(),
269    )
270    .unwrap()
271}
272
273#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
274pub(crate) fn default_hostname() -> Url {
275    use std::str::FromStr;
276
277    Url::from_str(
278        web_sys::window()
279            .map(|w| w.location().host().ok())
280            .flatten()
281            .unwrap_or(String::from("http://localhost"))
282            .as_str(),
283    )
284    .unwrap()
285}
286
287#[cfg(all(target_arch = "wasm32", target_os = "wasi"))]
288pub(crate) fn default_hostname() -> Url {
289    use std::str::FromStr;
290
291    Url::from_str("http://localhost").unwrap()
292}