use std::io;
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "cli")]
use tracing_subscriber::fmt::MakeWriter;
static TUI_QUIET: AtomicBool = AtomicBool::new(false);
pub fn set_tui_quiet(quiet: bool) {
TUI_QUIET.store(quiet, Ordering::Relaxed);
}
pub fn is_tui_quiet() -> bool {
TUI_QUIET.load(Ordering::Relaxed)
}
pub struct TuiQuietGuard;
impl Drop for TuiQuietGuard {
fn drop(&mut self) {
set_tui_quiet(false);
}
}
pub fn suppress_tracing_for_tui() -> TuiQuietGuard {
set_tui_quiet(true);
TuiQuietGuard
}
#[derive(Debug)]
pub struct StderrOrNull;
impl io::Write for StderrOrNull {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
if TUI_QUIET.load(Ordering::Relaxed) {
return Ok(buf.len());
}
let mut handle = std::io::stderr().lock();
handle.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
if TUI_QUIET.load(Ordering::Relaxed) {
return Ok(());
}
std::io::stderr().flush()
}
}
#[derive(Clone, Debug)]
pub struct StderrOrNullMaker;
#[cfg(feature = "cli")]
impl<'a> MakeWriter<'a> for StderrOrNullMaker {
type Writer = StderrOrNull;
fn make_writer(&'a self) -> Self::Writer {
StderrOrNull
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use std::sync::{Mutex, MutexGuard};
static QUIET_TEST_LOCK: Mutex<()> = Mutex::new(());
fn quiet_test_lock() -> MutexGuard<'static, ()> {
QUIET_TEST_LOCK.lock().expect("quiet test lock poisoned")
}
#[test]
fn stderr_or_null_drops_bytes_when_quiet() {
let _l = quiet_test_lock();
set_tui_quiet(true);
let mut w = StderrOrNull;
let n = w.write(b"hello").unwrap();
assert_eq!(n, 5);
w.flush().unwrap();
set_tui_quiet(false);
}
#[test]
fn guard_restores_quiet_state_on_drop() {
let _l = quiet_test_lock();
assert!(!is_tui_quiet());
{
let _g = suppress_tracing_for_tui();
assert!(is_tui_quiet());
}
assert!(!is_tui_quiet());
}
#[test]
fn set_tui_quiet_stores_given_value() {
let _l = quiet_test_lock();
set_tui_quiet(true);
assert!(is_tui_quiet(), "must be quiet after set_tui_quiet(true)");
set_tui_quiet(false);
assert!(
!is_tui_quiet(),
"must not be quiet after set_tui_quiet(false)"
);
}
#[test]
fn guard_restores_even_if_another_guard_already_dropped() {
let _l = quiet_test_lock();
let _outer = suppress_tracing_for_tui();
assert!(is_tui_quiet());
{
let _inner = suppress_tracing_for_tui();
assert!(is_tui_quiet());
}
assert!(
!is_tui_quiet(),
"inner guard's drop already cleared the flag (documented)"
);
}
#[test]
fn stderr_or_null_write_not_quiet_returns_byte_count() {
let _l = quiet_test_lock();
set_tui_quiet(false);
let mut w = StderrOrNull;
let n = w.write(b"").unwrap();
assert_eq!(n, 0, "writing 0 bytes while not quiet must return 0");
}
#[test]
fn stderr_or_null_flush_not_quiet_succeeds() {
let _l = quiet_test_lock();
set_tui_quiet(false);
let mut w = StderrOrNull;
assert!(w.flush().is_ok(), "flush() when not quiet must succeed");
}
#[test]
fn suppress_tracing_sets_quiet_immediately() {
let _l = quiet_test_lock();
set_tui_quiet(false);
let _g = suppress_tracing_for_tui();
assert!(
is_tui_quiet(),
"suppress_tracing_for_tui must set quiet=true before returning"
);
drop(_g);
assert!(
!is_tui_quiet(),
"dropping the guard must restore quiet=false"
);
}
}