1use 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
12static 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
40pub fn log(msg: impl AsRef<str>) {
42 if quiet() {
43 return;
44 }
45 emit(format!("spar: {}", msg.as_ref()));
46}
47
48pub 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
60pub 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
69pub 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}