bc_envelope/extension/expressions/
event.rs1use bc_components::{ARID, tags};
2use dcbor::{Date, prelude::*};
3
4use crate::{Envelope, EnvelopeEncodable, Error, Result, known_values};
5
6#[derive(Debug, Clone, PartialEq)]
45pub struct Event<T>
46where
47 T: EnvelopeEncodable
48 + TryFrom<Envelope>
49 + std::fmt::Debug
50 + Clone
51 + PartialEq,
52{
53 content: T,
54 id: ARID,
55 note: String,
56 date: Option<Date>,
57}
58
59impl<T> std::fmt::Display for Event<T>
60where
61 T: EnvelopeEncodable
62 + TryFrom<Envelope>
63 + std::fmt::Debug
64 + Clone
65 + PartialEq,
66{
67 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69 write!(f, "Event({})", self.summary())
70 }
71}
72
73impl<T> Event<T>
74where
75 T: EnvelopeEncodable
76 + TryFrom<Envelope>
77 + std::fmt::Debug
78 + Clone
79 + PartialEq,
80{
81 pub fn summary(&self) -> String {
83 format!(
84 "id: {}, content: {}",
85 self.id.short_description(),
86 self.content.to_envelope().format_flat()
87 )
88 }
89}
90
91impl<T> Event<T>
92where
93 T: EnvelopeEncodable
94 + TryFrom<Envelope>
95 + std::fmt::Debug
96 + Clone
97 + PartialEq,
98{
99 pub fn new(content: impl Into<T>, id: ARID) -> Self {
116 Self {
117 content: content.into(),
118 id,
119 note: String::new(),
120 date: None,
121 }
122 }
123}
124
125pub trait EventBehavior<T>
130where
131 T: EnvelopeEncodable + TryFrom<Envelope>,
132{
133 fn with_note(self, note: impl Into<String>) -> Self;
141
142 fn with_date(self, date: Date) -> Self;
146
147 fn content(&self) -> &T;
153
154 fn id(&self) -> ARID;
156
157 fn note(&self) -> &str;
160
161 fn date(&self) -> Option<Date>;
163}
164
165impl<T> EventBehavior<T> for Event<T>
166where
167 T: EnvelopeEncodable
168 + TryFrom<Envelope>
169 + std::fmt::Debug
170 + Clone
171 + PartialEq,
172{
173 fn with_note(mut self, note: impl Into<String>) -> Self {
175 self.note = note.into();
176 self
177 }
178
179 fn with_date(mut self, date: Date) -> Self {
181 self.date = Some(date);
182 self
183 }
184
185 fn content(&self) -> &T { &self.content }
187
188 fn id(&self) -> ARID { self.id }
190
191 fn note(&self) -> &str { &self.note }
193
194 fn date(&self) -> Option<Date> { self.date }
196}
197
198impl<T> From<Event<T>> for Envelope
204where
205 T: EnvelopeEncodable
206 + TryFrom<Envelope>
207 + std::fmt::Debug
208 + Clone
209 + PartialEq,
210{
211 fn from(event: Event<T>) -> Self {
212 Envelope::new(CBOR::to_tagged_value(tags::TAG_EVENT, event.id))
213 .add_assertion(known_values::CONTENT, event.content.to_envelope())
214 .add_assertion_if(
215 !event.note.is_empty(),
216 known_values::NOTE,
217 event.note,
218 )
219 .add_optional_assertion(known_values::DATE, event.date)
220 }
221}
222
223impl<T> TryFrom<Envelope> for Event<T>
228where
229 T: EnvelopeEncodable
230 + TryFrom<Envelope>
231 + std::fmt::Debug
232 + Clone
233 + PartialEq,
234{
235 type Error = Error;
236
237 fn try_from(envelope: Envelope) -> Result<Self> {
238 let content_envelope =
239 envelope.object_for_predicate(known_values::CONTENT)?;
240 let content = T::try_from(content_envelope)
241 .map_err(|_| Error::msg("Failed to parse content"))?;
242 Ok(Self {
243 content,
244 id: envelope
245 .subject()
246 .try_leaf()?
247 .try_into_expected_tagged_value(tags::TAG_EVENT)?
248 .try_into()?,
249 note: envelope
250 .extract_optional_object_for_predicate(known_values::NOTE)?
251 .unwrap_or_default(),
252 date: envelope
253 .extract_optional_object_for_predicate(known_values::DATE)?,
254 })
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use hex_literal::hex;
261 use indoc::indoc;
262
263 use super::*;
264
265 fn request_id() -> ARID {
266 ARID::from_data(hex!(
267 "c66be27dbad7cd095ca77647406d07976dc0f35f0d4d654bb0e96dd227a1e9fc"
268 ))
269 }
270
271 #[test]
272 fn test_event() {
273 crate::register_tags();
274
275 let event_date = Date::try_from("2024-07-04T11:11:11Z").unwrap();
276 let event = Event::<String>::new("test", request_id())
277 .with_note("This is a test")
278 .with_date(event_date);
279
280 let envelope: Envelope = event.clone().into();
281 #[rustfmt::skip]
282 let expected = indoc!{r#"
283 event(ARID(c66be27d)) [
284 'content': "test"
285 'date': 2024-07-04T11:11:11Z
286 'note': "This is a test"
287 ]
288 "#}.trim();
289 assert_eq!(envelope.format(), expected);
290
291 let parsed_request = Event::<String>::try_from(envelope).unwrap();
292 assert_eq!(parsed_request.content(), "test");
293 assert_eq!(parsed_request.note(), "This is a test");
294 assert_eq!(parsed_request.date(), Some(event_date));
295 }
296}