use std::fmt::Display; use std::io::{self, Write}; /// Says what is happening, for work slow enough that silence looks like a hang. /// /// Everything goes to stderr, leaving stdout to the report a caller might pipe /// somewhere. /// /// The interesting part is [`start`] and [`finish`]: the name of a thing is /// written, and flushed, *before* the work begins, and its outcome lands on the /// same line afterwards. So a run that is still going says which item it is /// stuck on rather than merely that it has not finished — and a boot log read /// the next morning ends on the name of whatever was in progress when the /// machine gave up. /// /// [`start`]: Reporter::start /// [`finish`]: Reporter::finish pub struct Reporter { enabled: bool, } impl Reporter { pub fn to_stderr() -> Self { Self { enabled: true } } /// A reporter that says nothing. /// /// Only tests want this. Every command has a user in front of it, or a log /// behind it, and both are better off being told what is going on. #[cfg(test)] pub fn silent() -> Self { Self { enabled: false } } /// A heading for a stretch of work. pub fn phase(&self, message: impl Display) { if self.enabled { let _ = writeln!(io::stderr(), "{message}"); } } /// A whole line at once. /// /// One `writeln!` holds the lock for its whole call, so this stays legible /// when several threads report finishing at once — unlike [`start`], which /// leaves a line open and must not be interleaved. /// /// [`start`]: Reporter::start pub fn line(&self, message: impl Display) { if self.enabled { let _ = writeln!(io::stderr(), " {message}"); } } /// Name the thing about to be worked on, leaving the line open. /// /// Flushed, because the whole point is to appear before the waiting starts. /// Only one thread may have a line open at a time. pub fn start(&self, what: impl Display) { if self.enabled { let mut stderr = io::stderr(); let _ = write!(stderr, " {what} ... "); let _ = stderr.flush(); } } /// Close the line opened by [`start`]. /// /// [`start`]: Reporter::start pub fn finish(&self, outcome: impl Display) { if self.enabled { let _ = writeln!(io::stderr(), "{outcome}"); } } } /// Pick a word for a count, so reports do not say "1 directories". pub fn plural(count: usize, one: &'static str, many: &'static str) -> &'static str { if count == 1 { one } else { many } }