1use std::collections::VecDeque;
6use std::sync::Mutex;
7use std::time::SystemTime;
8
9use serde::Serialize;
10use serde_json::Error as SerdeError;
11
12#[derive(Debug)]
14pub enum ErrorHolderError {
15 TooLarge(usize),
16 Serde(SerdeError),
17}
18
19pub type Result<T> = std::result::Result<T, ErrorHolderError>;
21
22#[derive(Serialize, Default, Debug)]
24pub struct ErrorHolder {
25 max_errors: usize,
26 total_errors: usize,
27 max_size: usize,
28 total_size: usize,
29 errors: Mutex<VecDeque<String>>,
30}
31
32impl ErrorHolder {
33 pub fn new(max_errors: usize, max_size: usize) -> Self {
35 Self {
36 max_errors,
37 max_size,
38 total_errors: 0,
39 total_size: 0,
40 errors: Mutex::new(VecDeque::with_capacity(max_errors)),
41 }
42 }
43
44 pub fn push(&mut self, error: &str) -> Result<()> {
46 let mut guard = self.errors.lock().unwrap();
47 let formatted_error = format!("{} - {}", httpdate::fmt_http_date(SystemTime::now()), error);
48
49 loop {
50 if formatted_error.len() + self.total_size > self.max_size
51 || self.total_errors >= self.max_errors
52 {
53 let victim = guard.pop_front();
54 match victim {
55 Some(v) => {
56 self.total_size -= v.len();
57 self.total_errors -= 1;
58 }
59 None => return Err(ErrorHolderError::TooLarge(error.len())),
60 }
61 } else {
62 break;
63 }
64 }
65
66 self.total_size += formatted_error.len();
67 self.total_errors += 1;
68 guard.push_back(formatted_error);
69 Ok(())
70 }
71
72 pub fn export(&self) -> Result<String> {
74 let _guard = self.errors.lock().unwrap();
75 serde_json::to_string(self).map_err(ErrorHolderError::Serde)
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::{ErrorHolder, ErrorHolderError};
82
83 #[test]
84 fn test_overflow() {
85 let mut holder = ErrorHolder::new(10, 80);
86 let error_msg = "123456789";
87 let mut left = 16;
88 while left >= 0 {
89 let r = holder.push(error_msg);
90 assert!(r.is_ok());
91 left -= 1;
92 }
93
94 assert!(holder.total_errors <= 10);
95 assert!(holder.total_size <= 80);
96
97 let mut multi = 10;
98 let mut error_msg_long = "".to_string();
99 while multi >= 0 {
100 multi -= 1;
101 error_msg_long.push_str("123456789");
102 }
103
104 let r = holder.push(&error_msg_long);
105 match r {
106 Err(ErrorHolderError::TooLarge(len)) => assert_eq!(len, error_msg_long.len()),
107 _ => panic!(),
108 }
109 }
110}