bc_envelope/extension/expressions/
event.rs1use anyhow::{ Error, Result };
2use bc_components::{ tags, ARID };
3use dcbor::prelude::*;
4use dcbor::Date;
5
6use crate::{ known_values, Envelope, EnvelopeEncodable };
7
8#[derive(Debug, Clone, PartialEq)]
46pub struct Event<T>
47 where T: EnvelopeEncodable + TryFrom<Envelope> + std::fmt::Debug + Clone + PartialEq {
48 content: T,
49 id: ARID,
50 note: String,
51 date: Option<Date>,
52}
53
54impl<T> std::fmt::Display
55 for Event<T>
56 where T: EnvelopeEncodable + TryFrom<Envelope> + std::fmt::Debug + Clone + PartialEq
57{
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(f, "Event({})", self.summary())
61 }
62}
63
64impl<T> Event<T>
65 where T: EnvelopeEncodable + TryFrom<Envelope> + std::fmt::Debug + Clone + PartialEq
66{
67 pub fn summary(&self) -> String {
69 format!(
70 "id: {}, content: {}",
71 self.id.short_description(),
72 self.content.to_envelope().format_flat()
73 )
74 }
75}
76
77impl<T> Event<T>
78 where T: EnvelopeEncodable + TryFrom<Envelope> + std::fmt::Debug + Clone + PartialEq
79{
80 pub fn new(content: impl Into<T>, id: ARID) -> Self {
97 Self {
98 content: content.into(),
99 id,
100 note: String::new(),
101 date: None,
102 }
103 }
104}
105
106pub trait EventBehavior<T> where T: EnvelopeEncodable + TryFrom<Envelope> {
111 fn with_note(self, note: impl Into<String>) -> Self;
119
120 fn with_date(self, date: impl AsRef<Date>) -> Self;
124
125 fn content(&self) -> &T;
131
132 fn id(&self) -> ARID;
134
135 fn note(&self) -> &str;
137
138 fn date(&self) -> Option<&Date>;
140}
141
142impl<T> EventBehavior<T>
143 for Event<T>
144 where T: EnvelopeEncodable + TryFrom<Envelope> + std::fmt::Debug + Clone + PartialEq
145{
146 fn with_note(mut self, note: impl Into<String>) -> Self {
148 self.note = note.into();
149 self
150 }
151
152 fn with_date(mut self, date: impl AsRef<Date>) -> Self {
154 self.date = Some(date.as_ref().clone());
155 self
156 }
157
158 fn content(&self) -> &T {
160 &self.content
161 }
162
163 fn id(&self) -> ARID {
165 self.id
166 }
167
168 fn note(&self) -> &str {
170 &self.note
171 }
172
173 fn date(&self) -> Option<&Date> {
175 self.date.as_ref()
176 }
177}
178
179impl<T> From<Event<T>>
184 for Envelope
185 where T: EnvelopeEncodable + TryFrom<Envelope> + std::fmt::Debug + Clone + PartialEq
186{
187 fn from(event: Event<T>) -> Self {
188 Envelope::new(CBOR::to_tagged_value(tags::TAG_EVENT, event.id))
189 .add_assertion(known_values::CONTENT, event.content.to_envelope())
190 .add_assertion_if(!event.note.is_empty(), known_values::NOTE, event.note)
191 .add_optional_assertion(known_values::DATE, event.date)
192 }
193}
194
195impl<T> TryFrom<Envelope>
200 for Event<T>
201 where T: EnvelopeEncodable + TryFrom<Envelope> + std::fmt::Debug + Clone + PartialEq
202{
203 type Error = Error;
204
205 fn try_from(envelope: Envelope) -> Result<Self> {
206 let content_envelope = envelope.object_for_predicate(known_values::CONTENT)?;
207 let content = T::try_from(content_envelope).map_err(|_|
208 Error::msg("Failed to parse content")
209 )?;
210 Ok(Self {
211 content,
212 id: envelope
213 .subject()
214 .try_leaf()?
215 .try_into_expected_tagged_value(tags::TAG_EVENT)?
216 .try_into()?,
217 note: envelope
218 .extract_optional_object_for_predicate(known_values::NOTE)?
219 .unwrap_or_default(),
220 date: envelope.extract_optional_object_for_predicate(known_values::DATE)?,
221 })
222 }
223}
224
225#[cfg(test)]
226mod tests {
227 use super::*;
228 use hex_literal::hex;
229 use indoc::indoc;
230
231 fn request_id() -> ARID {
232 ARID::from_data(hex!("c66be27dbad7cd095ca77647406d07976dc0f35f0d4d654bb0e96dd227a1e9fc"))
233 }
234
235 #[test]
236 fn test_event() {
237 crate::register_tags();
238
239 let event_date = Date::try_from("2024-07-04T11:11:11Z").unwrap();
240 let event = Event::<String>
241 ::new("test", request_id())
242 .with_note("This is a test")
243 .with_date(&event_date);
244
245 let envelope: Envelope = event.clone().into();
246 #[rustfmt::skip]
247 let expected = indoc!{r#"
248 event(ARID(c66be27d)) [
249 'content': "test"
250 'date': 2024-07-04T11:11:11Z
251 'note': "This is a test"
252 ]
253 "#}.trim();
254 assert_eq!(envelope.format(), expected);
255
256 let parsed_request = Event::<String>::try_from(envelope).unwrap();
257 assert_eq!(parsed_request.content(), "test");
258 assert_eq!(parsed_request.note(), "This is a test");
259 assert_eq!(parsed_request.date(), Some(&event_date));
260 }
261}