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(&mut self, msg: LogMsg) {
60        match msg {
61            LogMsg::NoLogStore => {}
62            LogMsg::Note(s) => self.add_note(s),
63            LogMsg::Info(s) => self.add_info(s),
64            LogMsg::Warning(s) => self.add_warning(s),
65            LogMsg::Error(s) => self.add_error(s),
66        }
67    }
68    fn add_note<T: Into<String>>(&mut self, msg: T);
69    fn add_info<T: Into<String>>(&mut self, msg: T);
70    fn add_warning<T: Into<String>>(&mut self, msg: T);
71    fn add_error<T: Into<String>>(&mut self, msg: T);
72}
73
74// ---------------------------------------------------------------------------------------------
75
76/// Basic log system that prints out messages to stderr without storing them
77#[derive(Clone, Debug)]
78pub struct PrintLog {
79    num_notes: usize,
80    num_infos: usize,
81    num_warnings: usize,
82    num_errors: usize
83}
84
85impl PrintLog {
86    pub fn new() -> PrintLog {
87        PrintLog { num_notes: 0, num_infos: 0, num_warnings: 0, num_errors: 0}
88    }
89}
90
91impl Default for PrintLog {
92    fn default() -> Self {
93        Self::new()
94    }
95}
96
97impl LogStatus for PrintLog {
98    fn num_notes(&self) -> usize {
99        self.num_notes
100    }
101
102    fn num_infos(&self) -> usize {
103        self.num_infos
104    }
105
106    fn num_warnings(&self) -> usize {
107        self.num_warnings
108    }
109
110    fn num_errors(&self) -> usize {
111        self.num_errors
112    }
113}
114
115impl Logger for PrintLog {
116    fn add_note<T: Into<String>>(&mut self, msg: T) {
117        eprintln!("NOTE:    {}", msg.into());
118    }
119
120    fn add_info<T: Into<String>>(&mut self, msg: T) {
121        eprintln!("INFO:    {}", msg.into());
122    }
123
124    fn add_warning<T: Into<String>>(&mut self, msg: T) {
125        eprintln!("WARNING: {}", msg.into());
126    }
127
128    fn add_error<T: Into<String>>(&mut self, msg: T) {
129        eprintln!("ERROR:   {}", msg.into());
130    }
131}
132
133// ---------------------------------------------------------------------------------------------
134
135#[derive(Clone, Debug)]
136pub enum LogMsg { NoLogStore, Note(String), Info(String), Warning(String), Error(String) }
137
138impl LogMsg {
139    pub fn get_inner_str(&self) -> &str {
140        match self {
141            LogMsg::NoLogStore => "The log messages were not stored",
142            LogMsg::Note(s)
143            | LogMsg::Info(s)
144            | LogMsg::Warning(s)
145            | LogMsg::Error(s) => s.as_str()
146        }
147    }
148
149    pub fn get_inner_str_mut(&mut self) -> Option<&mut String> {
150        match self {
151            LogMsg::NoLogStore => None,
152            LogMsg::Note(s)
153            | LogMsg::Info(s)
154            | LogMsg::Warning(s)
155            | LogMsg::Error(s) => Some(s),
156        }
157    }
158}
159
160impl Display for LogMsg {
161    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
162        match self {
163            LogMsg::NoLogStore => write!(f, "{}", self.get_inner_str()),
164            LogMsg::Note(s) =>    write!(f, "Note   : {s}"),
165            LogMsg::Info(s) =>    write!(f, "Info   : {s}"),
166            LogMsg::Warning(s) => write!(f, "Warning: {s}"),
167            LogMsg::Error(s) =>   write!(f, "ERROR  : {s}"),
168        }
169    }
170}
171
172/// Log system that stores the messages
173#[derive(Clone, Debug)]
174pub struct BufLog {
175    messages: Vec<LogMsg>,
176    num_notes: usize,
177    num_infos: usize,
178    num_warnings: usize,
179    num_errors: usize
180}
181
182impl BufLog {
183    pub fn new() -> Self {
184        BufLog { messages: Vec::new(), num_notes: 0, num_infos: 0, num_warnings: 0, num_errors: 0 }
185    }
186
187    pub fn is_empty(&self) -> bool {
188        self.messages.is_empty()
189    }
190
191    /// Clears all messages: notes, warnings, and errors.
192    pub fn clear(&mut self) {
193        self.messages.clear();
194        self.num_notes = 0;
195        self.num_infos = 0;
196        self.num_warnings = 0;
197        self.num_errors = 0;
198    }
199
200    /// Extends the messages with another Logger's messages.
201    pub fn extend(&mut self, other: BufLog) {
202        self.num_notes += other.num_notes;
203        self.num_infos += other.num_infos;
204        self.num_warnings += other.num_warnings;
205        self.num_errors += other.num_errors;
206        self.messages.extend(other.messages)
207    }
208
209    pub fn extend_messages<T: IntoIterator<Item = LogMsg>>(&mut self, iter: T) {
210        self.messages.extend(iter.into_iter().inspect(|m| {
211            match m {
212                LogMsg::NoLogStore => {}
213                LogMsg::Note(_) => self.num_notes += 1,
214                LogMsg::Info(_) => self.num_infos += 1,
215                LogMsg::Warning(_) => self.num_warnings += 1,
216                LogMsg::Error(_) => self.num_errors += 1,
217            }
218        }));
219
220    }
221}
222
223impl LogStatus for BufLog {
224    fn num_notes(&self) -> usize {
225        self.num_notes
226    }
227
228    fn num_infos(&self) -> usize {
229        self.num_infos
230    }
231
232    fn num_warnings(&self) -> usize {
233        self.num_warnings
234    }
235
236    fn num_errors(&self) -> usize {
237        self.num_errors
238    }
239
240    fn get_messages(&self) -> impl Iterator<Item = &LogMsg> {
241        self.messages.iter()
242    }
243
244    fn get_messages_str(&self) -> String {
245        self.get_messages().map(|m| format!("- {m}")).collect::<Vec<_>>().join("\n")
246    }
247
248}
249
250impl Logger for BufLog {
251    fn add(&mut self, msg: LogMsg) {
252        match &msg {
253            LogMsg::NoLogStore => {}
254            LogMsg::Note(_) => { self.num_notes += 1; }
255            LogMsg::Info(_) => { self.num_infos += 1; }
256            LogMsg::Warning(_) => { self.num_warnings += 1; }
257            LogMsg::Error(_) => { self.num_errors += 1; }
258        }
259        self.messages.push(msg);
260    }
261
262    fn add_note<T: Into<String>>(&mut self, msg: T) {
263        self.messages.push(LogMsg::Note(msg.into()));
264        self.num_notes += 1;
265    }
266
267    fn add_info<T: Into<String>>(&mut self, msg: T) {
268        self.messages.push(LogMsg::Info(msg.into()));
269        self.num_infos += 1;
270    }
271
272    fn add_warning<T: Into<String>>(&mut self, msg: T) {
273        self.messages.push(LogMsg::Warning(msg.into()));
274        self.num_warnings += 1;
275    }
276
277    fn add_error<T: Into<String>>(&mut self, msg: T) {
278        self.messages.push(LogMsg::Error(msg.into()));
279        self.num_errors += 1;
280    }
281}
282
283impl Default for BufLog {
284    fn default() -> Self {
285        BufLog::new()
286    }
287}
288
289impl Display for BufLog {
290    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
291        writeln!(f, "{}", self.get_messages_str())?;
292        writeln!(f, "{}", self.get_totals())
293    }
294}
295
296// ---------------------------------------------------------------------------------------------
297// blanket implementations: LogReader -> LogStatus, LogWriter -> Logger
298
299pub trait LogReader {
300    type Item: LogStatus;
301
302    fn get_log(&self) -> &Self::Item;
303
304    fn give_log(self) -> Self::Item;
305}
306
307pub trait LogWriter {
308    fn get_log_mut(&mut self) -> &mut impl Logger;
309}
310
311impl<T: LogReader + Debug> LogStatus for T {
312    fn num_notes(&self) -> usize {
313        self.get_log().num_notes()
314    }
315
316    fn num_infos(&self) -> usize {
317        self.get_log().num_infos()
318    }
319
320    fn num_warnings(&self) -> usize {
321        self.get_log().num_warnings()
322    }
323
324    fn num_errors(&self) -> usize {
325        self.get_log().num_errors()
326    }
327
328    fn has_no_errors(&self) -> bool {
329        self.get_log().has_no_errors()
330    }
331
332    fn has_no_warnings(&self) -> bool {
333        self.get_log().has_no_warnings()
334    }
335
336    fn get_messages(&self) -> impl Iterator<Item=&LogMsg> {
337        self.get_log().get_messages()
338    }
339
340    fn get_messages_str(&self) -> String {
341        self.get_log().get_messages_str()
342    }
343}
344
345impl<L: LogWriter + Debug> Logger for L {
346    fn add(&mut self, msg: LogMsg) {
347        self.get_log_mut().add(msg);
348    }
349
350    fn add_note<T: Into<String>>(&mut self, msg: T) {
351        self.get_log_mut().add_note(msg);
352    }
353
354    fn add_info<T: Into<String>>(&mut self, msg: T) {
355        self.get_log_mut().add_info(msg);
356    }
357
358    fn add_warning<T: Into<String>>(&mut self, msg: T) {
359        self.get_log_mut().add_warning(msg);
360    }
361
362    fn add_error<T: Into<String>>(&mut self, msg: T) {
363        self.get_log_mut().add_error(msg);
364    }
365}
366
367// ---------------------------------------------------------------------------------------------------------
368