1use crate::instant::Instant;
25use crate::parking_lot::Mutex;
26#[cfg(target_arch = "wasm32")]
27use crate::wasm_bindgen::{self, prelude::*};
28use crate::{reflect::prelude::*, visitor::prelude::*};
29use fxhash::FxHashMap;
30use std::collections::hash_map::Entry;
31use std::fmt::{Debug, Display};
32#[cfg(not(target_arch = "wasm32"))]
33use std::io::{self, Write};
34use std::path::Path;
35use std::sync::mpsc::Sender;
36use std::sync::LazyLock;
37use std::time::Duration;
38
39#[cfg(target_arch = "wasm32")]
40#[wasm_bindgen]
41extern "C" {
42 #[wasm_bindgen(js_namespace = console)]
45 fn log(s: &str);
46}
47
48pub struct LogMessage {
50 pub kind: MessageKind,
52 pub content: String,
54 pub time: Duration,
57}
58
59static LOG: LazyLock<Mutex<Log>> = LazyLock::new(|| {
60 Mutex::new(Log {
61 #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
62 file: None,
63 verbosity: MessageKind::Information,
64 listeners: Default::default(),
65 time_origin: Instant::now(),
66 one_shot_sources: Default::default(),
67 })
68});
69
70#[derive(Debug, Default, Copy, Clone, PartialOrd, PartialEq, Eq, Ord, Hash, Visit, Reflect)]
72#[repr(u32)]
73pub enum MessageKind {
74 #[default]
76 Information = 0,
77 Warning = 1,
79 Error = 2,
81}
82
83impl MessageKind {
84 fn as_str(self) -> &'static str {
85 match self {
86 MessageKind::Information => "[INFO]: ",
87 MessageKind::Warning => "[WARNING]: ",
88 MessageKind::Error => "[ERROR]: ",
89 }
90 }
91}
92
93pub struct Log {
95 #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
96 file: Option<std::fs::File>,
97 verbosity: MessageKind,
98 listeners: Vec<Sender<LogMessage>>,
99 time_origin: Instant,
100 one_shot_sources: FxHashMap<usize, String>,
101}
102
103impl Log {
104 pub fn set_file_name<P: AsRef<Path>>(#[allow(unused_variables)] path: P) {
106 #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
107 {
108 let mut guard = LOG.lock();
109 guard.file = std::fs::File::create(path).ok();
110 }
111 }
112
113 pub fn set_file(#[allow(unused_variables)] file: Option<std::fs::File>) {
115 #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
116 {
117 let mut guard = LOG.lock();
118 guard.file = file;
119 }
120 }
121
122 fn write_internal<S>(&mut self, id: Option<usize>, kind: MessageKind, message: S) -> bool
123 where
124 S: AsRef<str>,
125 {
126 let mut msg = message.as_ref().to_owned();
127 if kind as u32 >= self.verbosity as u32 {
128 if let Some(id) = id {
129 let mut need_write = false;
130 match self.one_shot_sources.entry(id) {
131 Entry::Occupied(mut message) => {
132 if message.get() != &msg {
133 message.insert(msg.clone());
134 need_write = true;
135 }
136 }
137 Entry::Vacant(entry) => {
138 entry.insert(msg.clone());
139 need_write = true;
140 }
141 }
142
143 if !need_write {
144 return false;
145 }
146 }
147
148 self.listeners.retain(|listener| {
150 listener
151 .send(LogMessage {
152 kind,
153 content: msg.clone(),
154 time: Instant::now() - self.time_origin,
155 })
156 .is_ok()
157 });
158
159 msg.insert_str(0, kind.as_str());
160
161 #[cfg(target_arch = "wasm32")]
162 {
163 log(&msg);
164 }
165
166 #[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
167 {
168 let _ = io::stdout().write_all(msg.as_bytes());
169
170 if let Some(log_file) = self.file.as_mut() {
171 let _ = log_file.write_all(msg.as_bytes());
172 let _ = log_file.flush();
173 }
174 }
175
176 #[cfg(target_os = "android")]
177 {
178 let _ = io::stdout().write_all(msg.as_bytes());
179 }
180 }
181
182 true
183 }
184
185 fn writeln_internal<S>(&mut self, id: Option<usize>, kind: MessageKind, message: S) -> bool
186 where
187 S: AsRef<str>,
188 {
189 let mut msg = message.as_ref().to_owned();
190 msg.push('\n');
191 self.write_internal(id, kind, msg)
192 }
193
194 pub fn write<S>(kind: MessageKind, msg: S)
196 where
197 S: AsRef<str>,
198 {
199 LOG.lock().write_internal(None, kind, msg);
200 }
201
202 pub fn write_once<S>(id: usize, kind: MessageKind, msg: S) -> bool
207 where
208 S: AsRef<str>,
209 {
210 LOG.lock().write_internal(Some(id), kind, msg)
211 }
212
213 pub fn writeln<S>(kind: MessageKind, msg: S)
216 where
217 S: AsRef<str>,
218 {
219 LOG.lock().writeln_internal(None, kind, msg);
220 }
221
222 pub fn writeln_once<S>(id: usize, kind: MessageKind, msg: S) -> bool
225 where
226 S: AsRef<str>,
227 {
228 LOG.lock().writeln_internal(Some(id), kind, msg)
229 }
230
231 pub fn info<S>(msg: S)
233 where
234 S: AsRef<str>,
235 {
236 Self::writeln(MessageKind::Information, msg)
237 }
238
239 pub fn warn<S>(msg: S)
241 where
242 S: AsRef<str>,
243 {
244 Self::writeln(MessageKind::Warning, msg)
245 }
246
247 pub fn err<S>(msg: S)
249 where
250 S: AsRef<str>,
251 {
252 Self::writeln(MessageKind::Error, msg)
253 }
254
255 pub fn info_once<S>(id: usize, msg: S) -> bool
257 where
258 S: AsRef<str>,
259 {
260 Self::writeln_once(id, MessageKind::Information, msg)
261 }
262
263 pub fn warn_once<S>(id: usize, msg: S) -> bool
265 where
266 S: AsRef<str>,
267 {
268 Self::writeln_once(id, MessageKind::Warning, msg)
269 }
270
271 pub fn err_once<S>(id: usize, msg: S) -> bool
273 where
274 S: AsRef<str>,
275 {
276 Self::writeln_once(id, MessageKind::Error, msg)
277 }
278
279 pub fn set_verbosity(kind: MessageKind) {
281 LOG.lock().verbosity = kind;
282 }
283
284 pub fn add_listener(listener: Sender<LogMessage>) {
286 LOG.lock().listeners.push(listener)
287 }
288
289 pub fn verify<T, E>(result: Result<T, E>)
296 where
297 E: Debug,
298 {
299 if let Err(e) = result {
300 Self::writeln(
301 MessageKind::Error,
302 format!("Operation failed! Reason: {e:?}"),
303 );
304 }
305 }
306
307 pub fn verify_message<S, T, E>(result: Result<T, E>, msg: S)
314 where
315 E: Debug,
316 S: Display,
317 {
318 if let Err(e) = result {
319 Self::writeln(MessageKind::Error, format!("{msg}. Reason: {e:?}"));
320 }
321 }
322}
323
324#[macro_export]
325macro_rules! info {
326 ($($arg:tt)*) => {
327 $crate::log::Log::info(format!($($arg)*))
328 };
329}
330
331#[macro_export]
332macro_rules! warn {
333 ($($arg:tt)*) => {
334 $crate::log::Log::warn(format!($($arg)*))
335 };
336}
337
338#[macro_export]
339macro_rules! err {
340 ($($arg:tt)*) => {
341 $crate::log::Log::err(format!($($arg)*))
342 };
343}
344
345#[macro_export]
346macro_rules! info_once {
347 ($id:expr, $($arg:tt)*) => {
348 $crate::log::Log::info_once($id, format!($($arg)*))
349 };
350}
351
352#[macro_export]
353macro_rules! warn_once {
354 ($id:expr, $($arg:tt)*) => {
355 $crate::log::Log::warn_once($id, format!($($arg)*))
356 };
357}
358
359#[macro_export]
360macro_rules! err_once {
361 ($id:expr, $($arg:tt)*) => {
362 $crate::log::Log::err_once($id, format!($($arg)*))
363 };
364}