hex_patch/app/log/
logger.rs

1use 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<T: Into<String>>(&mut self, level: NotificationLevel, message: T) {
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.into()));
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<T: Into<String>>(&mut self, level: NotificationLevel, message: T) {
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}