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 pub fn from_str(s: &str) -> Self {
25 match s.to_lowercase().as_str() {
26 "low" => Severity::Low,
27 "high" => Severity::High,
28 "critical" => Severity::Critical,
29 _ => Severity::Medium,
30 }
31 }
32}
33
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct EventSource {
36 pub source_type: String,
37 pub name: String,
38 pub callback: Option<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct EventChannel {
43 pub id: String,
44 pub name: String,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct EventSender {
49 pub id: String,
50 pub name: String,
51 pub sender_type: String,
52}
53
54#[derive(Debug, Clone, Serialize, Deserialize)]
55pub struct EventContent {
56 pub text: String,
57 pub content_type: String,
58 pub severity: Option<Severity>,
59 pub data: Option<Value>,
60}
61
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct Event {
64 pub id: String,
65 pub timestamp: DateTime<Utc>,
66 pub source: EventSource,
67 pub channel: Option<EventChannel>,
68 pub sender: Option<EventSender>,
69 pub content: EventContent,
70 pub expects_response: bool,
71 pub reply_to: Option<String>,
72}
73
74impl Event {
75 pub fn simple(source_type: &str, text: &str, severity: Option<Severity>) -> Self {
77 Event {
78 id: Uuid::new_v4().to_string(),
79 timestamp: Utc::now(),
80 source: EventSource {
81 source_type: source_type.to_string(),
82 name: source_type.to_string(),
83 callback: None,
84 },
85 channel: None,
86 sender: None,
87 content: EventContent {
88 text: text.to_string(),
89 content_type: "message".to_string(),
90 severity,
91 data: None,
92 },
93 expects_response: false,
94 reply_to: None,
95 }
96 }
97}
98
99#[cfg(test)]
100mod tests {
101 use super::*;
102
103 #[test]
104 fn severity_ordering() {
105 assert!(Severity::Low < Severity::Medium);
106 assert!(Severity::Medium < Severity::High);
107 assert!(Severity::High < Severity::Critical);
108 }
109
110 #[test]
111 fn severity_str_roundtrip() {
112 for s in [Severity::Low, Severity::Medium, Severity::High, Severity::Critical] {
113 assert_eq!(Severity::from_str(s.as_str()), s);
114 }
115 assert_eq!(Severity::from_str("garbage"), Severity::Medium);
116 }
117
118 #[test]
119 fn event_simple_defaults() {
120 let e = Event::simple("cli", "hello", Some(Severity::High));
121 assert_eq!(e.source.source_type, "cli");
122 assert_eq!(e.content.text, "hello");
123 assert_eq!(e.content.severity, Some(Severity::High));
124 assert!(!e.expects_response);
125 assert!(e.channel.is_none());
126 }
127
128 #[test]
129 fn event_serde_roundtrip() {
130 let e = Event::simple("discord", "yo", Some(Severity::Critical));
131 let json = serde_json::to_string(&e).unwrap();
132 let back: Event = serde_json::from_str(&json).unwrap();
133 assert_eq!(back.id, e.id);
134 assert_eq!(back.content.text, "yo");
135 assert_eq!(back.content.severity, Some(Severity::Critical));
136 }
137}