Skip to main content

spar/
logging.rs

1//! Progress goes to stderr so stdout stays parseable. `--quiet` silences
2//! progress but never silences a warning or an error: those are exactly what
3//! quiet mode would otherwise bury.
4
5use std::io::Write;
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::sync::Mutex;
8
9static QUIET: AtomicBool = AtomicBool::new(false);
10static NO_COLOR: AtomicBool = AtomicBool::new(false);
11
12/// Serialises writes so two triage threads cannot interleave mid-line.
13static STDERR_LOCK: Mutex<()> = Mutex::new(());
14
15pub fn set_quiet(value: bool) {
16    QUIET.store(value, Ordering::Relaxed);
17}
18
19pub fn quiet() -> bool {
20    QUIET.load(Ordering::Relaxed)
21}
22
23pub fn init_color() {
24    let disabled = std::env::var_os("NO_COLOR").is_some()
25        || std::env::var("TERM").map(|t| t == "dumb").unwrap_or(false);
26    NO_COLOR.store(disabled, Ordering::Relaxed);
27}
28
29fn colored() -> bool {
30    !NO_COLOR.load(Ordering::Relaxed)
31}
32
33fn emit(line: String) {
34    let _guard = STDERR_LOCK.lock().unwrap_or_else(|e| e.into_inner());
35    let mut err = std::io::stderr().lock();
36    let _ = writeln!(err, "{line}");
37    let _ = err.flush();
38}
39
40/// Progress. Suppressed by `--quiet`.
41pub fn log(msg: impl AsRef<str>) {
42    if quiet() {
43        return;
44    }
45    emit(format!("spar: {}", msg.as_ref()));
46}
47
48/// Secondary progress: true but not interesting. Suppressed by `--quiet`.
49pub fn dim(msg: impl AsRef<str>) {
50    if quiet() {
51        return;
52    }
53    if colored() {
54        emit(format!("\x1b[2mspar: {}\x1b[0m", msg.as_ref()));
55    } else {
56        emit(format!("spar: {}", msg.as_ref()));
57    }
58}
59
60/// Serious but not fatal. Never suppressed.
61pub fn warn(msg: impl AsRef<str>) {
62    if colored() {
63        emit(format!("\x1b[33mspar: WARNING:\x1b[0m {}", msg.as_ref()));
64    } else {
65        emit(format!("spar: WARNING: {}", msg.as_ref()));
66    }
67}
68
69/// Fatal. Never suppressed.
70pub fn error(msg: impl AsRef<str>) {
71    if colored() {
72        emit(format!("\x1b[31mspar:\x1b[0m {}", msg.as_ref()));
73    } else {
74        emit(format!("spar: {}", msg.as_ref()));
75    }
76}
77
78#[macro_export]
79macro_rules! log {
80    ($($arg:tt)*) => { $crate::logging::log(format!($($arg)*)) };
81}
82
83#[macro_export]
84macro_rules! logdim {
85    ($($arg:tt)*) => { $crate::logging::dim(format!($($arg)*)) };
86}
87
88#[macro_export]
89macro_rules! logwarn {
90    ($($arg:tt)*) => { $crate::logging::warn(format!($($arg)*)) };
91}