1use owo_colors::OwoColorize;
2use std::fmt::Arguments;
3use std::io::{self, Write};
4use std::sync::Mutex;
5use unicode_width::UnicodeWidthStr;
6
7#[derive(Clone, Copy, Debug)]
8pub enum Level { Info, Warn, Crit }
9
10impl Level {
11 fn as_str(self) -> &'static str {
12 match self { Level::Info => "INFO", Level::Warn => "WARN", Level::Crit => "CRIT" }
13 }
14}
15
16pub struct Logger {
17 obj_width: usize,
18 writer: Mutex<Box<dyn Write + Send>>,
19 gap_after_colon: usize,
20}
21
22impl Logger {
23 pub fn new(obj_width: usize) -> Self {
24 Self {
25 obj_width,
26 writer: Mutex::new(Box::new(io::stdout())),
27 gap_after_colon: 4,
28 }
29 }
30
31 pub fn log(&self, level: Level, obj: &str, args: Arguments) {
32 let level_tag = level.as_str();
33 let obj_padded = pad_obj(obj, self.obj_width);
34
35 let lvl_out = level_tag.red().to_string();
36 let obj_out = obj_padded.green().to_string();
37
38 let mut guard = self.writer.lock().unwrap();
39 let _ = write!(
40 &mut *guard,
41 "[{}] [{}]:{}{msg}\n",
42 lvl_out,
43 obj_out,
44 " ".repeat(self.gap_after_colon),
45 msg = format!("{}", args)
46 );
47 let _ = guard.flush();
48 }
49
50 pub fn info(&self, obj: &str, args: Arguments) { self.log(Level::Info, obj, args) }
51 pub fn warn(&self, obj: &str, args: Arguments) { self.log(Level::Warn, obj, args) }
52 pub fn crit(&self, obj: &str, args: Arguments) { self.log(Level::Crit, obj, args) }
53}
54
55fn pad_obj(obj: &str, width_cols: usize) -> String {
56 let mut s = obj.to_string();
57 while UnicodeWidthStr::width(s.as_str()) > width_cols {
58 s.pop();
59 }
60 let w = UnicodeWidthStr::width(s.as_str());
61 if w < width_cols {
62 s.push_str(&" ".repeat(width_cols - w));
63 }
64 s
65}
66
67#[macro_export] macro_rules! info { ($lg:expr, $obj:expr, $($arg:tt)*) => { $lg.info($obj, format_args!($($arg)*)) } }
68#[macro_export] macro_rules! warn { ($lg:expr, $obj:expr, $($arg:tt)*) => { $lg.warn($obj, format_args!($($arg)*)) } }
69#[macro_export] macro_rules! crit { ($lg:expr, $obj:expr, $($arg:tt)*) => { $lg.crit($obj, format_args!($($arg)*)) } }