Skip to main content

lexigram_core/
log.rs

1// Copyright (c) 2025 Redglyph (@gmail.com). All Rights Reserved.
2
3use std::fmt::{Debug, Display, Formatter};
4// use crate::{BuildError, HasBuildErrorSource};
5
6static NO_LOG_STORE: LogMsg = LogMsg::NoLogStore;
7
8/// Common log functionalities for a message consumer/status verifyier
9pub trait LogStatus: Debug {
10    fn num_notes(&self) -> usize;
11    fn num_infos(&self) -> usize;
12    fn num_warnings(&self) -> usize;
13    fn num_errors(&self) -> usize;
14    #[inline]
15    fn has_no_errors(&self) -> bool {
16        self.num_errors() == 0
17    }
18    #[inline]
19    fn has_no_warnings(&self) -> bool {
20        self.num_warnings() == 0
21    }
22
23    fn get_messages(&self) -> impl Iterator<Item = &LogMsg> {
24        [&NO_LOG_STORE].into_iter() // should we panic instead?
25    }
26
27    fn get_messages_str(&self) -> String {
28        self.get_messages().map(|m| format!("- {m}")).collect::<Vec<_>>().join("\n")
29    }
30
31    fn get_notes(&self) -> impl Iterator<Item = &LogMsg> {
32        self.get_messages().filter(|m| matches!(m, LogMsg::Note(_)))
33    }
34
35    fn get_infos(&self) -> impl Iterator<Item = &LogMsg> {
36        self.get_messages().filter(|m| matches!(m, LogMsg::Info(_)))
37    }
38
39    fn get_warnings(&self) -> impl Iterator<Item = &LogMsg> {
40        self.get_messages().filter(|m| matches!(m, LogMsg::Warning(_)))
41    }
42
43    fn get_errors(&self) -> impl Iterator<Item = &LogMsg> {
44        self.get_messages().filter(|m| matches!(m, LogMsg::Error(_)))
45    }
46
47    fn get_totals(&self) -> String {
48        format!(
49            "{} note(s)\n{} info(s)\n{} warning(s)\n{} error(s)",
50            self.num_notes(),
51            self.num_infos(),
52            self.num_warnings(),
53            self.num_errors())
54    }
55}
56
57/// Common log functionalities for a message producer
58pub trait Logger: Debug {
59    fn add_note<T: Into<String>>(&mut self, msg: T);
60    fn add_info<T: Into<String>>(&mut self, msg: T);
61    fn add_warning<T: Into<String>>(&mut self, msg: T);
62    fn add_error<T: Into<String>>(&mut self, msg: T);
63}
64
65// ---------------------------------------------------------------------------------------------
66
67/// Basic log system that prints out messages to stderr without storing them
68#[derive(Clone, Debug)]
69pub struct PrintLog {
70    num_notes: usize,
71    num_infos: usize,
72    num_warnings: usize,
73    num_errors: usize
74}
75
76impl PrintLog {
77    pub fn new() -> PrintLog {
78        PrintLog { num_notes: 0, num_infos: 0, num_warnings: 0, num_errors: 0}
79    }
80}
81
82impl Default for PrintLog {
83    fn default() -> Self {
84        Self::new()
85    }
86}
87
88impl LogStatus for PrintLog {
89    fn num_notes(&self) -> usize {
90        self.num_notes
91    }
92
93    fn num_infos(&self) -> usize {
94        self.num_infos
95    }
96
97    fn num_warnings(&self) -> usize {
98        self.num_warnings
99    }
100
101    fn num_errors(&self) -> usize {
102        self.num_errors
103    }
104}
105
106impl Logger for PrintLog {
107    fn add_note<T: Into<String>>(&mut self, msg: T) {
108        eprintln!("NOTE:    {}", msg.into());
109    }
110
111    fn add_info<T: Into<String>>(&mut self, msg: T) {
112        eprintln!("INFO:    {}", msg.into());
113    }
114
115    fn add_warning<T: Into<String>>(&mut self, msg: T) {
116        eprintln!("WARNING: {}", msg.into());
117    }
118
119    fn add_error<T: Into<String>>(&mut self, msg: T) {
120        eprintln!("ERROR:   {}", msg.into());
121    }
122}
123
124// ---------------------------------------------------------------------------------------------
125
126#[derive(Clone, Debug)]
127pub enum LogMsg { NoLogStore, Note(String), Info(String), Warning(String), Error(String) }
128
129impl LogMsg {
130    pub fn get_inner_str(&self) -> &str {
131        match self {
132            LogMsg::NoLogStore => "The log messages were not stored",
133            LogMsg::Note(s)
134            | LogMsg::Info(s)
135            | LogMsg::Warning(s)
136            | LogMsg::Error(s) => s.as_str()
137        }
138    }
139}
140impl Display for LogMsg {
141    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
142        match self {
143            LogMsg::NoLogStore => write!(f, "{}", self.get_inner_str()),
144            LogMsg::Note(s) =>    write!(f, "Note   : {s}"),
145            LogMsg::Info(s) =>    write!(f, "Info   : {s}"),
146            LogMsg::Warning(s) => write!(f, "Warning: {s}"),
147            LogMsg::Error(s) =>   write!(f, "ERROR  : {s}"),
148        }
149    }
150}
151/// Log system that stores the messages
152#[derive(Clone, Debug)]
153pub struct BufLog {
154    messages: Vec<LogMsg>,
155    num_notes: usize,
156    num_infos: usize,
157    num_warnings: usize,
158    num_errors: usize
159}
160
161impl BufLog {
162    pub fn new() -> Self {
163        BufLog { messages: Vec::new(), num_notes: 0, num_infos: 0, num_warnings: 0, num_errors: 0 }
164    }
165
166    pub fn is_empty(&self) -> bool {
167        self.messages.is_empty()
168    }
169
170    /// Clears all messages: notes, warnings, and errors.
171    pub fn clear(&mut self) {
172        self.messages.clear();
173        self.num_notes = 0;
174        self.num_infos = 0;
175        self.num_warnings = 0;
176        self.num_errors = 0;
177    }
178
179    /// Extends the messages with another Logger's messages.
180    pub fn extend(&mut self, other: BufLog) {
181        self.num_notes += other.num_notes;
182        self.num_infos += other.num_infos;
183        self.num_warnings += other.num_warnings;
184        self.num_errors += other.num_errors;
185        self.messages.extend(other.messages)
186    }
187
188    pub fn extend_messages<T: IntoIterator<Item = LogMsg>>(&mut self, iter: T) {
189        self.messages.extend(iter.into_iter().inspect(|m| {
190            match m {
191                LogMsg::NoLogStore => {}
192                LogMsg::Note(_) => self.num_notes += 1,
193                LogMsg::Info(_) => self.num_infos += 1,
194                LogMsg::Warning(_) => self.num_warnings += 1,
195                LogMsg::Error(_) => self.num_errors += 1,
196            }
197        }));
198
199    }
200}
201
202impl LogStatus for BufLog {
203    fn num_notes(&self) -> usize {
204        self.num_notes
205    }
206
207    fn num_infos(&self) -> usize {
208        self.num_infos
209    }
210
211    fn num_warnings(&self) -> usize {
212        self.num_warnings
213    }
214
215    fn num_errors(&self) -> usize {
216        self.num_errors
217    }
218
219    fn get_messages(&self) -> impl Iterator<Item = &LogMsg> {
220        self.messages.iter()
221    }
222
223    fn get_messages_str(&self) -> String {
224        self.get_messages().map(|m| format!("- {m}")).collect::<Vec<_>>().join("\n")
225    }
226
227}
228
229impl Logger for BufLog {
230    fn add_note<T: Into<String>>(&mut self, msg: T) {
231        self.messages.push(LogMsg::Note(msg.into()));
232        self.num_notes += 1;
233    }
234
235    fn add_info<T: Into<String>>(&mut self, msg: T) {
236        self.messages.push(LogMsg::Info(msg.into()));
237        self.num_infos += 1;
238    }
239
240    fn add_warning<T: Into<String>>(&mut self, msg: T) {
241        self.messages.push(LogMsg::Warning(msg.into()));
242        self.num_warnings += 1;
243    }
244
245    fn add_error<T: Into<String>>(&mut self, msg: T) {
246        self.messages.push(LogMsg::Error(msg.into()));
247        self.num_errors += 1;
248    }
249}
250
251impl Default for BufLog {
252    fn default() -> Self {
253        BufLog::new()
254    }
255}
256
257impl Display for BufLog {
258    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
259        writeln!(f, "{}", self.get_messages_str())?;
260        writeln!(f, "{}", self.get_totals())
261    }
262}
263
264// ---------------------------------------------------------------------------------------------
265// blanket implementations: LogReader -> LogStatus, LogWriter -> Logger
266
267pub trait LogReader {
268    type Item: LogStatus;
269
270    fn get_log(&self) -> &Self::Item;
271
272    fn give_log(self) -> Self::Item;
273}
274
275pub trait LogWriter {
276    fn get_log_mut(&mut self) -> &mut impl Logger;
277}
278
279impl<T: LogReader + Debug> LogStatus for T {
280    fn num_notes(&self) -> usize {
281        self.get_log().num_notes()
282    }
283
284    fn num_infos(&self) -> usize {
285        self.get_log().num_infos()
286    }
287
288    fn num_warnings(&self) -> usize {
289        self.get_log().num_warnings()
290    }
291
292    fn num_errors(&self) -> usize {
293        self.get_log().num_errors()
294    }
295
296    fn has_no_errors(&self) -> bool {
297        self.get_log().has_no_errors()
298    }
299
300    fn has_no_warnings(&self) -> bool {
301        self.get_log().has_no_warnings()
302    }
303
304    fn get_messages(&self) -> impl Iterator<Item=&LogMsg> {
305        self.get_log().get_messages()
306    }
307
308    fn get_messages_str(&self) -> String {
309        self.get_log().get_messages_str()
310    }
311}
312
313impl<L: LogWriter + Debug> Logger for L {
314    fn add_note<T: Into<String>>(&mut self, msg: T) {
315        self.get_log_mut().add_note(msg);
316    }
317
318    fn add_info<T: Into<String>>(&mut self, msg: T) {
319        self.get_log_mut().add_info(msg);
320    }
321
322    fn add_warning<T: Into<String>>(&mut self, msg: T) {
323        self.get_log_mut().add_warning(msg);
324    }
325
326    fn add_error<T: Into<String>>(&mut self, msg: T) {
327        self.get_log_mut().add_error(msg);
328    }
329}
330
331// ---------------------------------------------------------------------------------------------------------
332