1use std::fmt::{Debug, Display};
19
20use crate::common::{Formatter, Location, Pretty};
21
22#[derive(Default, Debug, Clone)]
23pub struct LogMsgFormatter {
24 pub newline: bool,
25 pub show_level: bool,
26}
27
28impl Formatter<LogMsg> for LogMsgFormatter {
29 fn format(&self, value: &LogMsg) -> String {
30 let msg = if let Some(location) = &value.location {
31 format!("{location}: {}", value.msg)
32 } else {
33 value.msg.clone()
34 };
35
36 if self.show_level {
37 value.level().to_string() + ": " + &msg
38 } else {
39 msg
40 }
41 }
42}
43
44#[derive(Debug, Default, Clone, PartialEq, PartialOrd)]
45pub enum LogLevel {
46 Trace,
47 Debug,
48 Warn,
49 #[default]
50 Error,
51}
52
53impl Display for LogLevel {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 match self {
56 LogLevel::Trace => write!(f, "Trace"),
57 LogLevel::Debug => write!(f, "Debug"),
58 LogLevel::Warn => write!(f, "Warn"),
59 LogLevel::Error => write!(f, "Error"),
60 }
61 }
62}
63
64impl From<LogLevel> for log::Level {
65 fn from(value: LogLevel) -> Self {
66 match value {
67 LogLevel::Trace => log::Level::Trace,
68 LogLevel::Debug => log::Level::Debug,
69 LogLevel::Warn => log::Level::Warn,
70 LogLevel::Error => log::Level::Error,
71 }
72 }
73}
74
75#[derive(Debug, Default)]
76pub struct LogMsg {
77 msg: String,
78 location: Option<Box<dyn Location>>,
79 level: LogLevel,
80}
81
82impl LogMsg {
83 pub fn new(msg: &str, level: LogLevel) -> Self {
84 Self::new_with_location(msg, level, None)
85 }
86
87 pub fn new_with_location(
88 msg: &str,
89 level: LogLevel,
90 location: Option<Box<dyn Location>>,
91 ) -> Self {
92 LogMsg {
93 msg: msg.to_string(),
94 level,
95 location,
96 }
97 }
98
99 pub fn level(&self) -> LogLevel {
100 self.level.clone()
101 }
102
103 pub fn msg(&self) -> String {
104 self.msg.clone()
105 }
106
107 pub fn location(&self) -> &Option<Box<dyn Location>> {
108 &self.location
109 }
110}
111
112#[derive(Debug, Default)]
113pub struct LogMsgs {
114 msgs: Vec<LogMsg>,
115 level: LogLevel,
116 prefix: Option<String>,
117}
118
119impl LogMsgs {
120 pub fn new(level: LogLevel) -> Self {
121 LogMsgs {
122 msgs: vec![],
123 level,
124 prefix: None,
125 }
126 }
127
128 pub fn new_with_prefix(prefix: &str, level: LogLevel) -> Self {
129 LogMsgs {
130 msgs: vec![],
131 level,
132 prefix: Some(prefix.to_string()),
133 }
134 }
135
136 pub fn update_level(self, new_level: LogLevel) -> Self {
137 LogMsgs {
138 msgs: self.msgs,
139 level: new_level,
140 prefix: self.prefix.clone(),
141 }
142 }
143
144 pub fn log(self, msg: LogMsg) -> Self {
145 if msg.level >= self.level {
146 let msg = if let Some(prefix) = &self.prefix {
147 LogMsg {
148 msg: format!("{prefix}: {}", msg.msg),
149 location: msg.location,
150 level: msg.level,
151 }
152 } else {
153 msg
154 };
155 let mut ns = self;
156 ns.msgs.push(msg);
157 ns
158 } else {
159 self
160 }
161 }
162
163 pub fn count(&self) -> usize {
164 self.msgs.len()
165 }
166
167 pub fn msgs(&self, formatter: &LogMsgFormatter) -> String {
168 self.msgs
169 .iter()
170 .map(|msg| msg.pretty(formatter))
171 .collect::<Vec<_>>()
172 .join(if formatter.newline { "\n" } else { ";" })
173 }
174
175 pub fn use_msgs(&self) -> &Vec<LogMsg> {
176 &self.msgs
177 }
178}
179
180macro_rules! emit_ {
181 ($nameloc:ident, $name:ident, $level:path) => {
182 #[allow(unused_macros)]
183 macro_rules! $nameloc {
184 ($log:expr, $loc:expr, $msg:expr ) => {
185 $log.log(LogMsg::new_with_location(
186 $msg,
187 $level,
188 Some(Box::new($loc)),
189 ))
190 };
191 }
192
193 #[allow(unused_macros)]
194 macro_rules! $name {
195 ($log:expr, $msg:expr ) => {
196 $log.log(LogMsg::new($msg, $level))
197 };
198 }
199 };
200}
201
202emit_!(trace_with_loc, trace, LogLevel::Trace);
203emit_!(debug_with_loc, debug, LogLevel::Debug);
204emit_!(warn_with_loc, warn, LogLevel::Warn);
205emit_!(error_with_loc, error, LogLevel::Error);