1use log::{Level, LevelFilter, Log, Metadata, Record};
2use std::io::{self, Write};
3
4#[cfg(unix)]
5use std::sync::LazyLock;
6#[cfg(unix)]
7use supports_color::{Stream, on};
8
9static CLI_LOGGER: CliLogger = CliLogger;
10pub struct CliLogger;
11
12#[cfg(unix)]
13static STDERR_ON_COLOR: LazyLock<bool> = LazyLock::new(|| on(Stream::Stderr).is_some());
14#[cfg(unix)]
15static STDOUT_ON_COLOR: LazyLock<bool> = LazyLock::new(|| on(Stream::Stdout).is_some());
16
17impl CliLogger {
18 pub fn init() {
19 log::set_logger(&CLI_LOGGER).unwrap();
20 log::set_max_level(LevelFilter::Info);
21 }
22
23 #[cfg(unix)]
24 fn prf_prefix(level: Level) -> &'static str {
25 match level {
26 Level::Error if *STDERR_ON_COLOR => "\x1b[31m",
27 Level::Error => "",
28 Level::Warn if *STDERR_ON_COLOR => "\x1b[33m",
29 Level::Warn => "",
30 Level::Debug if *STDOUT_ON_COLOR => "\x1b[34m",
31 Level::Debug => "",
32 Level::Trace if *STDOUT_ON_COLOR => "\x1b[35m",
33 Level::Trace => "",
34 _ => "",
35 }
36 }
37
38 #[cfg(windows)]
39 fn prf_prefix(_level: Level) -> &'static str {
40 ""
41 }
42
43 fn prf_suffix(level: Level) -> &'static str {
44 #[cfg(unix)]
45 match level {
46 Level::Error | Level::Warn if *STDERR_ON_COLOR => "\x1b[0m: ",
47 Level::Error | Level::Warn => ": ",
48 Level::Debug | Level::Trace if *STDOUT_ON_COLOR => "\x1b[0m: ",
49 Level::Debug | Level::Trace => ": ",
50 _ => "",
51 }
52
53 #[cfg(windows)]
54 match level {
55 Level::Error | Level::Warn | Level::Debug | Level::Trace => ": ",
56 _ => "",
57 }
58 }
59}
60
61impl Log for CliLogger {
62 fn enabled(&self, metadata: &Metadata) -> bool {
63 metadata.target().starts_with("fb2_clean")
64 }
65
66 fn log(&self, record: &Record) {
67 if !self.enabled(record.metadata()) {
68 return;
69 }
70
71 let level = record.level();
72
73 let msg = format!(
74 "{}{}{}{}\n",
75 Self::prf_prefix(level),
76 crate::log_prefix_root(level),
77 Self::prf_suffix(level),
78 record.args()
79 );
80 let msg = msg.as_bytes();
81
82 match level {
83 Level::Error | Level::Warn => {
84 let _ = io::stderr()
85 .write_all(msg)
86 .or_else(|_| io::stdout().write_all(msg));
87 }
88 _ => {
89 let _ = io::stdout().write_all(msg);
90 }
91 }
92 }
93
94 fn flush(&self) {}
95}