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_warnings(&self) -> usize;
12    fn num_errors(&self) -> usize;
13    #[inline]
14    fn has_no_errors(&self) -> bool {
15        self.num_errors() == 0
16    }
17    #[inline]
18    fn has_no_warnings(&self) -> bool {
19        self.num_warnings() == 0
20    }
21
22    fn get_messages(&self) -> impl Iterator<Item = &LogMsg> {
23        [&NO_LOG_STORE].into_iter() // should we panic instead?
24    }
25
26    fn get_messages_str(&self) -> String {
27        self.get_messages().map(|m| format!("- {m}")).collect::<Vec<_>>().join("\n")
28    }
29
30    fn get_notes(&self) -> impl Iterator<Item = &String> {
31        self.get_messages().filter_map(|m| if let LogMsg::Note(s) = m { Some(s) } else { None })
32    }
33
34    fn get_warnings(&self) -> impl Iterator<Item = &String> {
35        self.get_messages().filter_map(|m| if let LogMsg::Warning(s) = m { Some(s) } else { None })
36    }
37
38    fn get_errors(&self) -> impl Iterator<Item = &String> {
39        self.get_messages().filter_map(|m| if let LogMsg::Error(s) = m { Some(s) } else { None })
40    }
41}
42
43/// Common log functionalities for a message producer
44pub trait Logger: Debug {
45    fn add_note<T: Into<String>>(&mut self, msg: T);
46    fn add_warning<T: Into<String>>(&mut self, msg: T);
47    fn add_error<T: Into<String>>(&mut self, msg: T);
48}
49
50// ---------------------------------------------------------------------------------------------
51
52/// Basic log system that prints out messages to stderr without storing them
53#[derive(Clone, Debug)]
54pub struct PrintLog {
55    num_notes: usize,
56    num_warnings: usize,
57    num_errors: usize
58}
59
60impl PrintLog {
61    pub fn new() -> PrintLog {
62        PrintLog { num_notes: 0, num_warnings: 0, num_errors: 0}
63    }
64}
65
66impl LogStatus for PrintLog {
67    fn num_notes(&self) -> usize {
68        self.num_notes
69    }
70
71    fn num_warnings(&self) -> usize {
72        self.num_warnings
73    }
74
75    fn num_errors(&self) -> usize {
76        self.num_errors
77    }
78}
79
80impl Logger for PrintLog {
81    fn add_note<T: Into<String>>(&mut self, msg: T) {
82        eprintln!("NOTE:    {}", msg.into());
83    }
84
85    fn add_warning<T: Into<String>>(&mut self, msg: T) {
86        eprintln!("WARNING: {}", msg.into());
87    }
88
89    fn add_error<T: Into<String>>(&mut self, msg: T) {
90        eprintln!("ERROR:   {}", msg.into());
91    }
92}
93
94// ---------------------------------------------------------------------------------------------
95
96#[derive(Clone, Debug)]
97pub enum LogMsg { NoLogStore, Note(String), Warning(String), Error(String) }
98
99impl Display for LogMsg {
100    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
101        match self {
102            LogMsg::NoLogStore => write!(f, "The log messages were not stored"),
103            LogMsg::Note(s) =>    write!(f, "Note   : {s}"),
104            LogMsg::Warning(s) => write!(f, "Warning: {s}"),
105            LogMsg::Error(s) =>   write!(f, "ERROR  : {s}"),
106        }
107    }
108}
109/// Log system that stores the messages
110#[derive(Clone, Debug)]
111pub struct BufLog {
112    messages: Vec<LogMsg>,
113    num_notes: usize,
114    num_warnings: usize,
115    num_errors: usize
116}
117
118impl BufLog {
119    pub fn new() -> Self {
120        BufLog { messages: Vec::new(), num_notes: 0, num_warnings: 0, num_errors: 0 }
121    }
122
123    pub fn is_empty(&self) -> bool {
124        self.messages.is_empty()
125    }
126
127    /// Clears all messages: notes, warnings, and errors.
128    pub fn clear(&mut self) {
129        self.messages.clear();
130        self.num_notes = 0;
131        self.num_warnings = 0;
132        self.num_errors = 0;
133    }
134
135    /// Extends the messages with another Logger's messages.
136    pub fn extend(&mut self, other: BufLog) {
137        self.num_notes += other.num_notes;
138        self.num_warnings += other.num_warnings;
139        self.num_errors += other.num_errors;
140        self.messages.extend(other.messages)
141    }
142
143    pub fn extend_messages<T: IntoIterator<Item = LogMsg>>(&mut self, iter: T) {
144        self.messages.extend(iter.into_iter().inspect(|m| {
145            match m {
146                LogMsg::NoLogStore => {}
147                LogMsg::Note(_) => self.num_notes += 1,
148                LogMsg::Warning(_) => self.num_warnings += 1,
149                LogMsg::Error(_) => self.num_errors += 1,
150            }
151        }));
152
153    }
154}
155
156impl LogStatus for BufLog {
157    fn num_notes(&self) -> usize {
158        self.num_notes
159    }
160
161    fn num_warnings(&self) -> usize {
162        self.num_warnings
163    }
164
165    fn num_errors(&self) -> usize {
166        self.num_errors
167    }
168
169    fn get_messages(&self) -> impl Iterator<Item = &LogMsg> {
170        self.messages.iter()
171    }
172
173    fn get_messages_str(&self) -> String {
174        self.get_messages().map(|m| format!("- {m}")).collect::<Vec<_>>().join("\n")
175    }
176
177}
178
179impl Logger for BufLog {
180    fn add_note<T: Into<String>>(&mut self, msg: T) {
181        self.messages.push(LogMsg::Note(msg.into()));
182        self.num_notes += 1;
183    }
184
185    fn add_warning<T: Into<String>>(&mut self, msg: T) {
186        self.messages.push(LogMsg::Warning(msg.into()));
187        self.num_warnings += 1;
188    }
189
190    fn add_error<T: Into<String>>(&mut self, msg: T) {
191        self.messages.push(LogMsg::Error(msg.into()));
192        self.num_errors += 1;
193    }
194}
195
196impl Default for BufLog {
197    fn default() -> Self {
198        BufLog::new()
199    }
200}
201
202impl Display for BufLog {
203    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
204        write!(f, "{}", self.get_messages_str())
205    }
206}
207
208// ---------------------------------------------------------------------------------------------
209// blanket implementations: LogReader -> LogStatus, LogWriter -> Logger
210
211pub trait LogReader {
212    type Item: LogStatus;
213
214    fn get_log(&self) -> &Self::Item;
215
216    fn give_log(self) -> Self::Item;
217}
218
219pub trait LogWriter {
220    fn get_mut_log(&mut self) -> &mut impl Logger;
221}
222
223impl<T: LogReader + Debug> LogStatus for T {
224    fn num_notes(&self) -> usize {
225        self.get_log().num_notes()
226    }
227
228    fn num_warnings(&self) -> usize {
229        self.get_log().num_warnings()
230    }
231
232    fn num_errors(&self) -> usize {
233        self.get_log().num_errors()
234    }
235
236    fn has_no_errors(&self) -> bool {
237        self.get_log().has_no_errors()
238    }
239
240    fn has_no_warnings(&self) -> bool {
241        self.get_log().has_no_warnings()
242    }
243
244    fn get_messages(&self) -> impl Iterator<Item=&LogMsg> {
245        self.get_log().get_messages()
246    }
247
248    fn get_messages_str(&self) -> String {
249        self.get_log().get_messages_str()
250    }
251}
252
253impl<L: LogWriter + Debug> Logger for L {
254    fn add_note<T: Into<String>>(&mut self, msg: T) {
255        self.get_mut_log().add_note(msg);
256    }
257
258    fn add_warning<T: Into<String>>(&mut self, msg: T) {
259        self.get_mut_log().add_warning(msg);
260    }
261
262    fn add_error<T: Into<String>>(&mut self, msg: T) {
263        self.get_mut_log().add_error(msg);
264    }
265}
266
267// ---------------------------------------------------------------------------------------------------------
268