1#![allow(dead_code)]
4
5use std::fmt;
8
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
11#[allow(dead_code)]
12pub enum ErrorSeverity {
13 Info,
14 Warning,
15 Error,
16 Fatal,
17}
18
19impl fmt::Display for ErrorSeverity {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 ErrorSeverity::Info => write!(f, "INFO"),
23 ErrorSeverity::Warning => write!(f, "WARNING"),
24 ErrorSeverity::Error => write!(f, "ERROR"),
25 ErrorSeverity::Fatal => write!(f, "FATAL"),
26 }
27 }
28}
29
30#[derive(Debug, Clone)]
32#[allow(dead_code)]
33pub struct ErrorEntry {
34 pub severity: ErrorSeverity,
35 pub category: String,
36 pub message: String,
37 pub code: u32,
38}
39
40#[derive(Debug)]
42#[allow(dead_code)]
43pub struct ErrorLog {
44 entries: Vec<ErrorEntry>,
45 capacity: usize,
46 total_pushed: u64,
47}
48
49#[allow(dead_code)]
51pub fn new_error_log(capacity: usize) -> ErrorLog {
52 ErrorLog {
53 entries: Vec::new(),
54 capacity,
55 total_pushed: 0,
56 }
57}
58
59#[allow(dead_code)]
61pub fn push_error(
62 log: &mut ErrorLog,
63 severity: ErrorSeverity,
64 category: &str,
65 message: &str,
66 code: u32,
67) {
68 if log.entries.len() >= log.capacity && !log.entries.is_empty() {
69 log.entries.remove(0);
70 }
71 log.entries.push(ErrorEntry {
72 severity,
73 category: category.to_string(),
74 message: message.to_string(),
75 code,
76 });
77 log.total_pushed += 1;
78}
79
80#[allow(dead_code)]
82pub fn count_by_severity(log: &ErrorLog, min: &ErrorSeverity) -> usize {
83 log.entries.iter().filter(|e| &e.severity >= min).count()
84}
85
86#[allow(dead_code)]
88pub fn entries_for_category<'a>(log: &'a ErrorLog, category: &str) -> Vec<&'a ErrorEntry> {
89 log.entries
90 .iter()
91 .filter(|e| e.category == category)
92 .collect()
93}
94
95#[allow(dead_code)]
97pub fn clear_error_log(log: &mut ErrorLog) {
98 log.entries.clear();
99}
100
101#[allow(dead_code)]
103pub fn total_pushed(log: &ErrorLog) -> u64 {
104 log.total_pushed
105}
106
107#[allow(dead_code)]
109pub fn error_entry_count(log: &ErrorLog) -> usize {
110 log.entries.len()
111}
112
113#[allow(dead_code)]
115pub fn has_fatal(log: &ErrorLog) -> bool {
116 log.entries
117 .iter()
118 .any(|e| e.severity == ErrorSeverity::Fatal)
119}
120
121#[allow(dead_code)]
123pub fn last_error(log: &ErrorLog) -> Option<&ErrorEntry> {
124 log.entries.last()
125}
126
127#[allow(dead_code)]
129pub fn error_log_to_json(log: &ErrorLog) -> String {
130 let items: Vec<String> = log
131 .entries
132 .iter()
133 .map(|e| {
134 format!(
135 r#"{{"severity":"{}","category":"{}","message":"{}","code":{}}}"#,
136 e.severity, e.category, e.message, e.code
137 )
138 })
139 .collect();
140 format!("[{}]", items.join(","))
141}
142
143#[cfg(test)]
144mod tests {
145 use super::*;
146
147 #[test]
148 fn test_push_and_count() {
149 let mut log = new_error_log(10);
150 push_error(&mut log, ErrorSeverity::Error, "net", "timeout", 1);
151 push_error(&mut log, ErrorSeverity::Warning, "io", "slow", 2);
152 assert_eq!(error_entry_count(&log), 2);
153 }
154
155 #[test]
156 fn test_capacity_eviction() {
157 let mut log = new_error_log(3);
158 for i in 0..5u32 {
159 push_error(&mut log, ErrorSeverity::Info, "cat", "msg", i);
160 }
161 assert_eq!(error_entry_count(&log), 3);
162 assert_eq!(log.entries[0].code, 2);
163 }
164
165 #[test]
166 fn test_count_by_severity() {
167 let mut log = new_error_log(10);
168 push_error(&mut log, ErrorSeverity::Info, "a", "x", 0);
169 push_error(&mut log, ErrorSeverity::Error, "b", "y", 1);
170 push_error(&mut log, ErrorSeverity::Fatal, "c", "z", 2);
171 assert_eq!(count_by_severity(&log, &ErrorSeverity::Error), 2);
172 }
173
174 #[test]
175 fn test_entries_for_category() {
176 let mut log = new_error_log(10);
177 push_error(&mut log, ErrorSeverity::Error, "net", "a", 1);
178 push_error(&mut log, ErrorSeverity::Error, "io", "b", 2);
179 assert_eq!(entries_for_category(&log, "net").len(), 1);
180 }
181
182 #[test]
183 fn test_has_fatal() {
184 let mut log = new_error_log(10);
185 push_error(&mut log, ErrorSeverity::Warning, "x", "y", 0);
186 assert!(!has_fatal(&log));
187 push_error(&mut log, ErrorSeverity::Fatal, "x", "z", 1);
188 assert!(has_fatal(&log));
189 }
190
191 #[test]
192 fn test_clear() {
193 let mut log = new_error_log(10);
194 push_error(&mut log, ErrorSeverity::Error, "x", "y", 0);
195 clear_error_log(&mut log);
196 assert_eq!(error_entry_count(&log), 0);
197 }
198
199 #[test]
200 fn test_total_pushed() {
201 let mut log = new_error_log(2);
202 push_error(&mut log, ErrorSeverity::Info, "a", "b", 0);
203 push_error(&mut log, ErrorSeverity::Info, "a", "b", 1);
204 push_error(&mut log, ErrorSeverity::Info, "a", "b", 2);
205 assert_eq!(total_pushed(&log), 3);
206 }
207
208 #[test]
209 fn test_last_error() {
210 let mut log = new_error_log(10);
211 assert!(last_error(&log).is_none());
212 push_error(&mut log, ErrorSeverity::Error, "cat", "last", 99);
213 assert_eq!(last_error(&log).map(|e| e.code), Some(99));
214 }
215
216 #[test]
217 fn test_json_output() {
218 let mut log = new_error_log(10);
219 push_error(&mut log, ErrorSeverity::Info, "sys", "ok", 0);
220 let j = error_log_to_json(&log);
221 assert!(j.contains("INFO"));
222 assert!(j.contains("sys"));
223 }
224
225 #[test]
226 fn test_severity_ordering() {
227 assert!(ErrorSeverity::Fatal > ErrorSeverity::Error);
228 assert!(ErrorSeverity::Error > ErrorSeverity::Warning);
229 assert!(ErrorSeverity::Warning > ErrorSeverity::Info);
230 }
231}