lumen_core/output.rs
1//! Saying things without staking the process on being heard.
2//!
3//! `println!` and `eprintln!` panic when the write fails, which is the right
4//! call for a program whose output is the result and the wrong one for
5//! anything that has to keep running. A process whose output goes to a pipe
6//! nobody reads any more, or that a supervisor started with its streams
7//! closed, hits a broken pipe the next time it says anything; a server dies
8//! there, in the middle of answering somebody. A lost line of log is the
9//! smaller loss.
10//!
11//! So diagnostics go out through here: the line is written whenever anything
12//! is listening, and dropped when nothing is.
13
14use std::fmt::Arguments;
15use std::io::Write;
16
17/// Write a line to stdout, or drop it.
18pub fn to_stdout(message: Arguments<'_>) {
19 let _ = writeln!(std::io::stdout(), "{message}");
20}
21
22/// Write a line to stderr, or drop it.
23pub fn to_stderr(message: Arguments<'_>) {
24 let _ = writeln!(std::io::stderr(), "{message}");
25}
26
27/// Tell the person running this something. Reads like `println!`, and cannot
28/// end the process.
29#[macro_export]
30macro_rules! say_line {
31 ($($arg:tt)*) => { $crate::output::to_stdout(format_args!($($arg)*)) };
32}
33
34/// Warn the person running this about something. Reads like `eprintln!`, and
35/// cannot end the process.
36#[macro_export]
37macro_rules! warn_line {
38 ($($arg:tt)*) => { $crate::output::to_stderr(format_args!($($arg)*)) };
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44
45 /// Whatever the write does, the caller carries on. A stream that is gone
46 /// cannot be arranged from inside the process that owns it, so the test
47 /// that serves through a closed pipe is the one that proves the rest.
48 #[test]
49 fn a_line_goes_out_without_the_caller_having_to_care() {
50 to_stdout(format_args!("lumen: {} {}", "a", 1));
51 to_stderr(format_args!("lumen: {} {}", "b", 2));
52 say_line!("lumen: {}", "said");
53 warn_line!("lumen: {}", "warned");
54 }
55}