use std::io::{self, ErrorKind, Write as _};
macro_rules! println {
() => {
$crate::stdout::write_line(::std::string::String::new())
};
($($arg:tt)*) => {
$crate::stdout::write_line(::std::format!($($arg)*))
};
}
macro_rules! print {
() => {
$crate::stdout::write_str(::std::string::String::new())
};
($($arg:tt)*) => {
$crate::stdout::write_str(::std::format!($($arg)*))
};
}
pub(crate) use print;
pub(crate) use println;
enum WriteOutcome {
Ok,
ClosedPipe,
Failed(io::Error),
}
fn classify(result: io::Result<()>) -> WriteOutcome {
match result {
Ok(()) => WriteOutcome::Ok,
Err(err) if err.kind() == ErrorKind::BrokenPipe => WriteOutcome::ClosedPipe,
Err(err) => WriteOutcome::Failed(err),
}
}
pub(crate) fn write_line(line: String) {
write_and_handle(|stdout| writeln!(stdout, "{line}"));
}
pub(crate) fn write_str(text: String) {
write_and_handle(|stdout| write!(stdout, "{text}"));
}
fn write_and_handle(write: impl FnOnce(&mut io::StdoutLock<'_>) -> io::Result<()>) {
let stdout = io::stdout();
let mut lock = stdout.lock();
match classify(write(&mut lock)) {
WriteOutcome::Ok => {}
WriteOutcome::ClosedPipe => std::process::exit(0),
WriteOutcome::Failed(err) => {
eprintln!("error: failed printing to stdout: {err}");
std::process::exit(1);
}
}
}
#[cfg(test)]
#[path = "stdout/tests.rs"]
mod tests;