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}
62
63#[derive(Debug, Clone, Serialize, Deserialize)]
64pub struct Event {
65 pub id: String,
66 pub timestamp: DateTime<Utc>,
67 pub source: EventSource,
68 pub channel: Option<EventChannel>,
69 pub sender: Option<EventSender>,
70 pub content: EventContent,
71 pub expects_response: bool,
72 pub reply_to: Option<String>,
73}
74
75impl Event {
76 pub fn simple(source_type: &str, text: &str, severity: Option<Severity>) -> Self {
78 Event {
79 id: Uuid::new_v4().to_string(),
80 timestamp: Utc::now(),
81 source: EventSource {
82 source_type: source_type.to_string(),
83 name: source_type.to_string(),
84 callback: None,
85 },
86 channel: None,
87 sender: None,
88 content: EventContent {
89 text: text.to_string(),
90 content_type: "message".to_string(),
91 severity,
92 data: None,
93 },
94 expects_response: false,
95 reply_to: None,
96 }
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn severity_ordering() {
106 assert!(Severity::Low < Severity::Medium);
107 assert!(Severity::Medium < Severity::High);
108 assert!(Severity::High < Severity::Critical);
109 }
110
111 #[test]
112 fn severity_str_roundtrip() {
113 for s in [Severity::Low, Severity::Medium, Severity::High, Severity::Critical] {
114 assert_eq!(Severity::from_str(s.as_str()), s);
115 }
116 assert_eq!(Severity::from_str("garbage"), Severity::Medium);
117 }
118
119 #[test]
120 fn event_simple_defaults() {
121 let e = Event::simple("cli", "hello", Some(Severity::High));
122 assert_eq!(e.source.source_type, "cli");
123 assert_eq!(e.content.text, "hello");
124 assert_eq!(e.content.severity, Some(Severity::High));
125 assert!(!e.expects_response);
126 assert!(e.channel.is_none());
127 }
128
129 #[test]
130 fn event_serde_roundtrip() {
131 let e = Event::simple("discord", "yo", Some(Severity::Critical));
132 let json = serde_json::to_string(&e).unwrap();
133 let back: Event = serde_json::from_str(&json).unwrap();
134 assert_eq!(back.id, e.id);
135 assert_eq!(back.content.text, "yo");
136 assert_eq!(back.content.severity, Some(Severity::Critical));
137 }
138}