hex_patch/app/log/
logger.rs1use std::{collections::VecDeque, ops::Index};
2
3use crate::app::{settings::verbosity::Verbosity, App};
4
5use super::{log_line::LogLine, notification::NotificationLevel};
6
7#[derive(Debug, Clone)]
8pub struct Logger {
9 pub(super) limit: usize,
10 pub(super) log: VecDeque<LogLine>,
11 pub(super) verbosity: Verbosity,
12 pub(super) notification: NotificationLevel,
13}
14
15impl Logger {
16 pub fn new(limit: usize, verbosity: Verbosity) -> Self {
17 Self {
18 limit,
19 log: VecDeque::with_capacity(limit),
20 verbosity,
21 notification: NotificationLevel::None,
22 }
23 }
24
25 pub fn log(&mut self, level: NotificationLevel, message: &str) {
26 if level >= self.verbosity.as_notification_level() {
27 self.notification.bump_notification_level(level);
28 if self.log.len() >= self.limit && self.limit > 0 {
29 self.log.pop_front();
30 }
31 self.log.push_back(LogLine::new(level, message.to_string()));
32 }
33 }
34
35 pub fn clear(&mut self) {
36 self.log.clear();
37 self.notification.reset();
38 }
39
40 pub fn get_notification_level(&self) -> NotificationLevel {
41 self.notification
42 }
43
44 pub fn len(&self) -> usize {
45 self.log.len()
46 }
47
48 pub fn iter(&self) -> impl DoubleEndedIterator<Item = &LogLine> + ExactSizeIterator {
49 self.log.iter()
50 }
51
52 pub fn is_empty(&self) -> bool {
53 self.log.is_empty()
54 }
55
56 pub fn reset_notification_level(&mut self) {
57 self.notification.reset();
58 }
59
60 pub fn change_limit(&mut self, limit: usize) {
61 self.limit = limit;
62 if self.log.len() > limit && limit > 0 {
63 self.log.drain(0..self.log.len() - limit);
64 }
65 if let Some(additional) = limit.checked_sub(self.log.capacity()) {
66 self.log.reserve(additional);
67 } else {
68 self.log.shrink_to_fit();
69 }
70 }
71
72 pub fn change_verbosity(&mut self, verbosity: Verbosity) {
73 self.verbosity = verbosity;
74 for log_line in &self.log {
75 if log_line.level < verbosity.as_notification_level() {
76 self.notification.bump_notification_level(log_line.level);
77 }
78 }
79 if self.notification < verbosity.as_notification_level() {
80 self.notification = NotificationLevel::None;
81 }
82 }
83
84 pub fn merge(&mut self, other: &Self) {
85 for log_line in &other.log {
86 if self.log.len() >= self.limit && self.limit > 0 {
87 self.log.pop_front();
88 }
89 self.log(log_line.level, &log_line.message);
90 }
91 }
92}
93
94impl Default for Logger {
95 fn default() -> Self {
96 Self {
97 limit: Default::default(),
98 log: Default::default(),
99 verbosity: Verbosity::Debug,
100 notification: Default::default(),
101 }
102 }
103}
104
105impl Index<usize> for Logger {
106 type Output = LogLine;
107
108 fn index(&self, index: usize) -> &Self::Output {
109 &self.log[index]
110 }
111}
112
113impl App {
114 pub(in crate::app) fn log(&mut self, level: NotificationLevel, message: &str) {
115 self.logger.log(level, message);
116 }
117}
118
119#[cfg(test)]
120mod test {
121 use super::*;
122
123 #[test]
124 fn test_logger() {
125 let mut logger = Logger::default();
126 assert_eq!(logger.len(), 0);
127 assert!(logger.is_empty());
128 logger.log(NotificationLevel::Error, "Test error message");
129 assert_eq!(logger.len(), 1);
130 assert!(!logger.is_empty());
131 assert_eq!(logger[0].level, NotificationLevel::Error);
132 assert_eq!(logger[0].message, "Test error message");
133 logger.clear();
134 assert_eq!(logger.len(), 0);
135 assert!(logger.is_empty());
136 }
137
138 #[test]
139 fn test_logger_merge() {
140 let mut logger1 = Logger::default();
141 let mut logger2 = Logger::default();
142 logger1.log(NotificationLevel::Error, "Test error message");
143 logger2.log(NotificationLevel::Warning, "Test warning message");
144 logger1.merge(&logger2);
145 assert_eq!(logger1.len(), 2);
146 assert_eq!(logger1[0].level, NotificationLevel::Error);
147 assert_eq!(logger1[0].message, "Test error message");
148 assert_eq!(logger1[1].level, NotificationLevel::Warning);
149 assert_eq!(logger1[1].message, "Test warning message");
150 assert_eq!(logger1.get_notification_level(), NotificationLevel::Error);
151 }
152
153 #[test]
154 fn test_logger_with_limit() {
155 let mut logger = Logger::new(5, Verbosity::Debug);
156 for i in 0..10 {
157 logger.log(
158 NotificationLevel::Error,
159 &format!("Test error message {}", i),
160 );
161 }
162 assert_eq!(logger.len(), 5);
163 assert_eq!(logger[0].message, "Test error message 5");
164 assert_eq!(logger[4].message, "Test error message 9");
165 }
166
167 #[test]
168 fn test_logger_change_limit() {
169 let mut logger = Logger::new(2, Verbosity::Debug);
170 logger.log(NotificationLevel::Error, "Test error message 1");
171 logger.log(NotificationLevel::Error, "Test error message 2");
172 logger.log(NotificationLevel::Error, "Test error message 3");
173 assert_eq!(logger.len(), 2);
174 assert_eq!(logger[0].message, "Test error message 2");
175 assert_eq!(logger[1].message, "Test error message 3");
176 logger.change_limit(3);
177 assert_eq!(logger.len(), 2);
178 assert_eq!(logger[0].message, "Test error message 2");
179 assert_eq!(logger[1].message, "Test error message 3");
180 logger.change_limit(1);
181 assert_eq!(logger.len(), 1);
182 assert_eq!(logger[0].message, "Test error message 3");
183 }
184}