1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4use uuid::Uuid;
5
6#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
7pub enum Severity {
8 Low,
9 Medium,
10 High,
11 Critical,
12}
13
14impl Severity {
15 pub fn as_str(&self) -> &str {
16 match self {
17 Severity::Low => "low",
18 Severity::Medium => "medium",
19 Severity::High => "high",
20 Severity::Critical => "critical",
21 }
22 }
23
24 #[allow(clippy::should_implement_trait)]
25 pub fn from_str(s: &str) -> Self {
26 match s.to_lowercase().as_str() {
27 "low" => Severity::Low,
28 "high" => Severity::High,
29 "critical" => Severity::Critical,
30 _ => Severity::Medium,
31 }
32 }
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36pub struct EventSource {
37 pub source_type: String,
38 pub name: String,
39 pub callback: Option<String>,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct EventChannel {
44 pub id: String,
45 pub name: String,
46}
47
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct EventSender {
50 pub id: String,
51 pub name: String,
52 pub sender_type: String,
53}
54
55#[derive(Debug, Clone, Serialize, Deserialize)]
56pub struct EventContent {
57 pub text: String,
58 pub content_type: String,
59 pub severity: Option<Severity>,
60 pub data: Option<Value>,
61 #[serde(default, skip_serializing_if = "Option::is_none")]
65 pub disclosure: Option<agent_core::disclosure::DisclosureClass>,
66}
67
68#[derive(Debug, Clone, Serialize, Deserialize)]
69pub struct Event {
70 pub id: String,
71 pub timestamp: DateTime<Utc>,
72 pub source: EventSource,
73 pub channel: Option<EventChannel>,
74 pub sender: Option<EventSender>,
75 pub content: EventContent,
76 pub expects_response: bool,
77 pub reply_to: Option<String>,
78}
79
80impl Event {
81 pub fn simple(source_type: &str, text: &str, severity: Option<Severity>) -> Self {
83 Event {
84 id: Uuid::new_v4().to_string(),
85 timestamp: Utc::now(),
86 source: EventSource {
87 source_type: source_type.to_string(),
88 name: source_type.to_string(),
89 callback: None,
90 },
91 channel: None,
92 sender: None,
93 content: EventContent {
94 text: text.to_string(),
95 content_type: "message".to_string(),
96 severity,
97 data: None,
98 disclosure: None,
99 },
100 expects_response: false,
101 reply_to: None,
102 }
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109
110 #[test]
111 fn severity_ordering() {
112 assert!(Severity::Low < Severity::Medium);
113 assert!(Severity::Medium < Severity::High);
114 assert!(Severity::High < Severity::Critical);
115 }
116
117 #[test]
118 fn severity_str_roundtrip() {
119 for s in [
120 Severity::Low,
121 Severity::Medium,
122 Severity::High,
123 Severity::Critical,
124 ] {
125 assert_eq!(Severity::from_str(s.as_str()), s);
126 }
127 assert_eq!(Severity::from_str("garbage"), Severity::Medium);
128 }
129
130 #[test]
131 fn event_simple_defaults() {
132 let e = Event::simple("cli", "hello", Some(Severity::High));
133 assert_eq!(e.source.source_type, "cli");
134 assert_eq!(e.content.text, "hello");
135 assert_eq!(e.content.severity, Some(Severity::High));
136 assert!(!e.expects_response);
137 assert!(e.channel.is_none());
138 }
139
140 #[test]
141 fn event_serde_roundtrip() {
142 let e = Event::simple("discord", "yo", Some(Severity::Critical));
143 let json = serde_json::to_string(&e).unwrap();
144 let back: Event = serde_json::from_str(&json).unwrap();
145 assert_eq!(back.id, e.id);
146 assert_eq!(back.content.text, "yo");
147 assert_eq!(back.content.severity, Some(Severity::Critical));
148 }
149}