use std::fmt;
use std::io::{self, ErrorKind, Write};
use std::process::ExitCode;
use std::sync::atomic::{AtomicU8, Ordering};
static STDOUT: AtomicU8 = AtomicU8::new(OPEN);
const OPEN: u8 = 0;
const CLOSED: u8 = 1;
const FAILED: u8 = 2;
macro_rules! outln {
($($argument:tt)*) => {
$crate::out::stdout_line(format_args!($($argument)*))
};
}
macro_rules! errln {
($($argument:tt)*) => {
$crate::out::stderr_line(format_args!($($argument)*))
};
}
pub(crate) use {errln, outln};
pub fn stdout_line(line: fmt::Arguments<'_>) {
if STDOUT.load(Ordering::Relaxed) != OPEN {
return;
}
let written = writeln!(io::stdout().lock(), "{line}");
settle(written);
}
pub fn stderr_line(line: fmt::Arguments<'_>) {
let _ = writeln!(io::stderr().lock(), "{line}");
}
pub fn finish(code: ExitCode) -> ExitCode {
if STDOUT.load(Ordering::Relaxed) == OPEN {
settle(io::stdout().lock().flush());
}
if STDOUT.load(Ordering::Relaxed) == FAILED {
ExitCode::FAILURE
} else {
code
}
}
fn settle(written: io::Result<()>) {
let Err(error) = written else {
return;
};
if error.kind() == ErrorKind::BrokenPipe {
STDOUT.store(CLOSED, Ordering::Relaxed);
} else {
STDOUT.store(FAILED, Ordering::Relaxed);
errln!("cannot write to standard output: {error}");
}
}