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