1use crate::parking_lot::Mutex;
25use std::fmt::{Debug, Display};
26
27use crate::instant::Instant;
28#[cfg(not(target_arch = "wasm32"))]
29use std::io::{self, Write};
30use std::path::Path;
31use std::sync::mpsc::Sender;
32use std::sync::LazyLock;
33use std::time::Duration;
34
35#[cfg(target_arch = "wasm32")]
36use crate::wasm_bindgen::{self, prelude::*};
37
38#[cfg(target_arch = "wasm32")]
39#[wasm_bindgen]
40extern "C" {
41 #[wasm_bindgen(js_namespace = console)]
44 fn log(s: &str);
45}
46
47pub struct LogMessage {
49 pub kind: MessageKind,
51 pub content: String,
53 pub time: Duration,
56}
57
58static LOG: LazyLock<Mutex<Log>> = LazyLock::new(|| {
59 Mutex::new(Log {
60 #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
61 file: None,
62 verbosity: MessageKind::Information,
63 listeners: Default::default(),
64 time_origin: Instant::now(),
65 })
66});
67
68#[derive(Copy, Clone, PartialOrd, PartialEq, Eq, Ord, Hash)]
70#[repr(u32)]
71pub enum MessageKind {
72 Information = 0,
74 Warning = 1,
76 Error = 2,
78}
79
80impl MessageKind {
81 fn as_str(self) -> &'static str {
82 match self {
83 MessageKind::Information => "[INFO]: ",
84 MessageKind::Warning => "[WARNING]: ",
85 MessageKind::Error => "[ERROR]: ",
86 }
87 }
88}
89
90pub struct Log {
92 #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
93 file: Option<std::fs::File>,
94 verbosity: MessageKind,
95 listeners: Vec<Sender<LogMessage>>,
96 time_origin: Instant,
97}
98
99impl Log {
100 pub fn set_file_name<P: AsRef<Path>>(#[allow(unused_variables)] path: P) {
102 #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
103 {
104 let mut guard = LOG.lock();
105 guard.file = std::fs::File::create(path).ok();
106 }
107 }
108
109 pub fn set_file(#[allow(unused_variables)] file: Option<std::fs::File>) {
111 #[cfg(all(not(target_arch = "wasm32"), not(target_os = "android")))]
112 {
113 let mut guard = LOG.lock();
114 guard.file = file;
115 }
116 }
117
118 fn write_internal<S>(&mut self, kind: MessageKind, message: S)
119 where
120 S: AsRef<str>,
121 {
122 let mut msg = message.as_ref().to_owned();
123 if kind as u32 >= self.verbosity as u32 {
124 self.listeners.retain(|listener| {
126 listener
127 .send(LogMessage {
128 kind,
129 content: msg.clone(),
130 time: Instant::now() - self.time_origin,
131 })
132 .is_ok()
133 });
134
135 msg.insert_str(0, kind.as_str());
136
137 #[cfg(target_arch = "wasm32")]
138 {
139 log(&msg);
140 }
141
142 #[cfg(all(not(target_os = "android"), not(target_arch = "wasm32")))]
143 {
144 let _ = io::stdout().write_all(msg.as_bytes());
145
146 if let Some(log_file) = self.file.as_mut() {
147 let _ = log_file.write_all(msg.as_bytes());
148 let _ = log_file.flush();
149 }
150 }
151
152 #[cfg(target_os = "android")]
153 {
154 let _ = io::stdout().write_all(msg.as_bytes());
155 }
156 }
157 }
158
159 fn writeln_internal<S>(&mut self, kind: MessageKind, message: S)
160 where
161 S: AsRef<str>,
162 {
163 let mut msg = message.as_ref().to_owned();
164 msg.push('\n');
165 self.write_internal(kind, msg)
166 }
167
168 pub fn write<S>(kind: MessageKind, msg: S)
170 where
171 S: AsRef<str>,
172 {
173 LOG.lock().write_internal(kind, msg);
174 }
175
176 pub fn writeln<S>(kind: MessageKind, msg: S)
178 where
179 S: AsRef<str>,
180 {
181 LOG.lock().writeln_internal(kind, msg);
182 }
183
184 pub fn info<S>(msg: S)
186 where
187 S: AsRef<str>,
188 {
189 Self::writeln(MessageKind::Information, msg)
190 }
191
192 pub fn warn<S>(msg: S)
194 where
195 S: AsRef<str>,
196 {
197 Self::writeln(MessageKind::Warning, msg)
198 }
199
200 pub fn err<S>(msg: S)
202 where
203 S: AsRef<str>,
204 {
205 Self::writeln(MessageKind::Error, msg)
206 }
207
208 pub fn set_verbosity(kind: MessageKind) {
210 LOG.lock().verbosity = kind;
211 }
212
213 pub fn add_listener(listener: Sender<LogMessage>) {
215 LOG.lock().listeners.push(listener)
216 }
217
218 pub fn verify<T, E>(result: Result<T, E>)
225 where
226 E: Debug,
227 {
228 if let Err(e) = result {
229 Self::writeln(
230 MessageKind::Error,
231 format!("Operation failed! Reason: {e:?}"),
232 );
233 }
234 }
235
236 pub fn verify_message<S, T, E>(result: Result<T, E>, msg: S)
243 where
244 E: Debug,
245 S: Display,
246 {
247 if let Err(e) = result {
248 Self::writeln(MessageKind::Error, format!("{msg}. Reason: {e:?}"));
249 }
250 }
251}
252
253#[macro_export]
254macro_rules! info {
255 ($($arg:tt)*) => {
256 $crate::log::Log::info(format!($($arg)*))
257 };
258}
259
260#[macro_export]
261macro_rules! warn {
262 ($($arg:tt)*) => {
263 $crate::log::Log::warn(format!($($arg)*))
264 };
265}
266
267#[macro_export]
268macro_rules! err {
269 ($($arg:tt)*) => {
270 $crate::log::Log::err(format!($($arg)*))
271 };
272}