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