1pub mod uuid_human_hash;
61
62pub fn pfx() -> Prefix {
72 Prefix::new()
73}
74
75#[macro_export]
76macro_rules! fatal {
77 ($($f:expr),+ ; $p:expr) => {
78 #[allow(unused_imports)]
79 use std::fmt::Write as _;
80 let _ = writeln!(
81 crate::log::_impl::log_fmt(crate::log::_impl::LogLevel::Fatal, $p),
82 $($f),+
83 );
84 };
85}
86pub use _impl::LogLevel;
87pub use _impl::Prefix;
88pub use _impl::logger;
89#[allow(unused)]
90pub use fatal;
91#[macro_export]
92macro_rules! error {
93 ($($f:expr),+ ; $p:expr) => {
94 #[allow(unused_imports)]
95 use std::fmt::Write as _;
96 let _ = writeln!(
97 crate::log::_impl::log_fmt(crate::log::_impl::LogLevel::Error, $p),
98 $($f),+
99 );
100 };
101}
102#[allow(unused)]
103pub use error;
104#[macro_export]
105macro_rules! warning {
106 ($($f:expr),+ ; $p:expr) => {
107 #[allow(unused_imports)]
108 use std::fmt::Write as _;
109 let _ = writeln!(
110 crate::log::_impl::log_fmt(crate::log::_impl::LogLevel::Warn, $p),
111 $($f),+
112 );
113 };
114}
115#[allow(unused)]
116pub use warning;
117#[macro_export]
118macro_rules! info {
119 ($($f:expr),+ ; $p:expr) => {
120 #[allow(unused_imports)]
121 use std::fmt::Write as _;
122 let _ = writeln!(
123 crate::log::_impl::log_fmt(crate::log::_impl::LogLevel::Info, $p),
124 $($f),+
125 );
126 };
127}
128#[allow(unused)]
129pub use info;
130#[macro_export]
131macro_rules! debug {
132 ($($f:expr),+ ; $p:expr) => {
133 #[allow(unused_imports)]
134 use std::fmt::Write as _;
135 let _ = writeln!(
136 crate::log::_impl::log_fmt(crate::log::_impl::LogLevel::Debug, $p),
137 $($f),+
138 );
139 };
140}
141#[allow(unused)]
142pub use debug;
143#[macro_export]
144macro_rules! debug_force {
145 ($($f:expr),+ ; $p:expr) => {
146 #[allow(unused_imports)]
147 use std::fmt::Write as _;
148 let _ = writeln!(
149 crate::log::_impl::LogFmtForce(crate::log::_impl::log_fmt(crate::log::_impl::LogLevel::Debug, $p)),
150 $($f),+
151 );
152 };
153}
154#[allow(unused)]
155pub use debug_force;
156
157pub mod _impl {
158
159 use std::{
160 io::{StderrLock, Write},
161 sync::LazyLock,
162 };
163
164 use crate::{lobby::state::clients::PlayerId, server::state::LobbyId};
165
166 static LOGGER: LazyLock<Log> = LazyLock::new(Log::new);
167
168 pub fn logger<'a>() -> &'a Log {
172 LazyLock::force(&LOGGER)
173 }
174
175 pub fn log_fmt<'a>(level: LogLevel, prefix: &'a Prefix) -> LogFmt<'a> {
176 LogFmt(std::io::stderr().lock(), Newline::First, prefix, level)
177 }
178
179 pub struct Log {
180 pub level: LogLevel,
181 pub colors: bool,
182 }
183
184 impl Log {
185 pub fn level(&self, level: LogLevel) -> bool {
187 self.level >= level
188 }
189 fn new() -> Self {
190 Self {
191 #[cfg(test)]
192 level: LogLevel::Debug,
193 #[cfg(not(test))]
194 level: match std::env::var("LOG_LEVEL")
195 .map(|v| v.to_lowercase())
196 .as_ref()
197 .map(|v| v.as_str())
198 .unwrap_or("warn")
199 {
200 "fatal" => LogLevel::Fatal,
201 "err" | "error" => LogLevel::Error,
202 "warn" | "warning" => LogLevel::Warn,
203 "info" => LogLevel::Info,
204 "debug" | "dbg" => LogLevel::Debug,
205 _ => LogLevel::Debug,
206 },
207 colors: match std::env::var("LOG_COLORS")
208 .map(|v| v.to_lowercase())
209 .as_ref()
210 .map(|v| v.as_str())
211 .unwrap_or("yes")
212 {
213 "yes" => true,
214 _ => false,
215 },
216 }
217 }
218 }
219
220 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
221 pub enum LogLevel {
222 Fatal,
223 Error,
224 Warn,
225 Info,
226 Debug,
227 }
228
229 #[derive(Clone, Copy)]
230 pub struct Prefix {
231 lobby: Option<LobbyId>,
232 player: Option<PlayerId>,
233 }
234
235 impl Prefix {
236 pub(super) fn new() -> Self {
237 Prefix {
238 lobby: None,
239 player: None,
240 }
241 }
242 pub fn lobby(&mut self, id: LobbyId) -> &mut Self {
243 self.lobby = Some(id);
244 self
245 }
246 pub fn no_lobby(&mut self) -> &mut Self {
247 self.lobby = None;
248 self
249 }
250 pub fn player(&mut self, id: PlayerId) -> &mut Self {
251 self.player = Some(id);
252 self
253 }
254 pub fn no_player(&mut self) -> &mut Self {
255 self.player = None;
256 self
257 }
258 }
259
260 impl LogLevel {
261 const fn defs(&self) -> [&'static str; 4] {
262 match self {
263 Self::Fatal => [">F", " F", "\x1b[1;31m>F\x1b[0m", "\x1b[1;31m F\x1b[0m"],
264 Self::Error => [">E", " E", "\x1b[0;31m>E\x1b[0m", "\x1b[0;31m E\x1b[0m"],
265 Self::Warn => [">W", " W", "\x1b[0;33m>W\x1b[0m", "\x1b[0;33m W\x1b[0m"],
266 Self::Info => [">I", " I", "\x1b[0;34m>I\x1b[0m", "\x1b[0;34m I\x1b[0m"],
267 Self::Debug => [">D", " D", "\x1b[0;90m>D", "\x1b[0;90m D"],
268 }
269 }
270 const fn pfx_simple_first(&self) -> &'static str {
271 self.defs()[0]
272 }
273 const fn pfx_simple_other(&self) -> &'static str {
274 self.defs()[1]
275 }
276 const fn pfx_colored_first(&self) -> &'static str {
277 self.defs()[2]
278 }
279 const fn pfx_colored_other(&self) -> &'static str {
280 self.defs()[3]
281 }
282 }
283 pub struct LogFmt<'a>(StderrLock<'a>, Newline, &'a Prefix, LogLevel);
284 pub struct LogFmtForce<'a>(pub LogFmt<'a>);
285
286 impl std::fmt::Write for LogFmt<'_> {
287 fn write_str(&mut self, s: &str) -> std::fmt::Result {
288 let Self(stderr, newline, prefix, level) = self;
289 let log = logger();
290 if !log.level(*level) {
291 return Ok(());
292 }
293 log_impl(s, *level, log, prefix, stderr, newline)
294 }
295 }
296 impl std::fmt::Write for LogFmtForce<'_> {
297 fn write_str(&mut self, s: &str) -> std::fmt::Result {
298 let Self(LogFmt(stderr, newline, prefix, level)) = self;
299 let log = logger();
300 log_impl(s, *level, log, prefix, stderr, newline)
302 }
303 }
304
305 enum Newline {
306 First,
307 Yes,
308 No,
309 }
310
311 fn log_impl<'a>(
312 s: &str,
313 level: LogLevel,
314 log: &Log,
315 prefix: &Prefix,
316 stderr: &mut StderrLock<'a>,
317 newline: &mut Newline,
318 ) -> std::fmt::Result {
319 if s.is_empty() {
320 return Ok(());
321 }
322 macro_rules! write_prefix {
323 () => {
324 if let Some(lobby) = &prefix.lobby {
325 let _ = write!(stderr, " L{lobby}");
326 }
327 if let Some(player) = &prefix.player {
328 let _ = write!(stderr, " P{player}");
329 }
330 let _ = write!(stderr, ": ");
331 };
332 }
333 if let Newline::First = newline {
334 let _ = stderr.write(
335 (if log.colors {
336 level.pfx_colored_first()
337 } else {
338 level.pfx_simple_first()
339 })
340 .as_bytes(),
341 );
342 write_prefix!();
343 *newline = Newline::No;
344 } else if let Newline::Yes = *newline {
345 let _ = stderr.write(
346 (if log.colors {
347 level.pfx_colored_other()
348 } else {
349 level.pfx_simple_other()
350 })
351 .as_bytes(),
352 );
353 write_prefix!();
354 *newline = Newline::No;
355 }
356 if let Some(i) = s.find('\n') {
357 let _ = stderr.write(s[0..=i].as_bytes());
358 *newline = Newline::Yes;
359 log_impl(&s[i + 1..], level, log, prefix, stderr, newline)?;
360 } else {
361 let _ = stderr.write(s.as_bytes());
362 }
363 Ok(())
364 }
365}