1extern crate alloc;
8use alloc::string::String;
9use core::fmt;
10
11use crate::{Header, Id128, SubstrateKind};
12
13#[derive(Clone, Debug)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
16pub struct Event {
17 #[cfg_attr(feature = "serde", serde(flatten))]
18 pub header: Header,
19 pub verb: String,
21 pub substrate: SubstrateKind,
23 pub actor: String,
25 pub outcome: EventOutcome,
27 pub data: Option<String>,
29 pub duration_us: u64,
31 pub target_id: Option<Id128>,
33}
34
35#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37#[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))]
38pub enum EventOutcome {
39 #[default]
40 Success,
41 Denied,
42 Error,
43}
44
45impl EventOutcome {
46 pub const fn name(self) -> &'static str {
47 match self {
48 Self::Success => "success",
49 Self::Denied => "denied",
50 Self::Error => "error",
51 }
52 }
53}
54
55impl fmt::Display for EventOutcome {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.write_str(self.name())
58 }
59}
60
61pub struct EventBuilder {
63 verb: String,
64 substrate: SubstrateKind,
65 actor: String,
66 outcome: EventOutcome,
67 data: Option<String>,
68 duration_us: u64,
69 target_id: Option<Id128>,
70}
71
72impl EventBuilder {
73 pub fn new(
74 verb: impl Into<String>,
75 substrate: SubstrateKind,
76 actor: impl Into<String>,
77 ) -> Self {
78 Self {
79 verb: verb.into(),
80 substrate,
81 actor: actor.into(),
82 outcome: EventOutcome::Success,
83 data: None,
84 duration_us: 0,
85 target_id: None,
86 }
87 }
88
89 pub fn outcome(mut self, outcome: EventOutcome) -> Self {
90 self.outcome = outcome;
91 self
92 }
93
94 pub fn data(mut self, data: impl Into<String>) -> Self {
95 self.data = Some(data.into());
96 self
97 }
98
99 pub fn duration_us(mut self, us: u64) -> Self {
100 self.duration_us = us;
101 self
102 }
103
104 pub fn target_id(mut self, id: Id128) -> Self {
105 self.target_id = Some(id);
106 self
107 }
108
109 pub fn build(self, header: Header) -> Event {
110 Event {
111 header,
112 verb: self.verb,
113 substrate: self.substrate,
114 actor: self.actor,
115 outcome: self.outcome,
116 data: self.data,
117 duration_us: self.duration_us,
118 target_id: self.target_id,
119 }
120 }
121}
122
123#[cfg(test)]
124mod tests {
125 use super::*;
126 use crate::{Namespace, Timestamp};
127
128 fn header() -> Header {
129 Header::new(
130 Id128::from_u128(1),
131 Namespace::default(),
132 Timestamp::from_secs(1700000000),
133 )
134 }
135
136 #[test]
137 fn event_builder() {
138 let event = EventBuilder::new("search", SubstrateKind::Note, "agent:research")
139 .outcome(EventOutcome::Success)
140 .duration_us(1500)
141 .target_id(Id128::from_u128(42))
142 .build(header());
143
144 assert_eq!(event.verb, "search");
145 assert_eq!(event.substrate, SubstrateKind::Note);
146 assert_eq!(event.actor, "agent:research");
147 assert_eq!(event.outcome, EventOutcome::Success);
148 assert_eq!(event.duration_us, 1500);
149 assert_eq!(event.target_id, Some(Id128::from_u128(42)));
150 }
151
152 #[test]
153 fn denied_outcome() {
154 let event = EventBuilder::new("create", SubstrateKind::Note, "user:ocean")
155 .outcome(EventOutcome::Denied)
156 .build(header());
157 assert_eq!(event.outcome, EventOutcome::Denied);
158 }
159}