use std::io::IsTerminal;
use std::process::{Command, Stdio};
use anyhow::Result;
pub const ANSI_RED: &str = "\x1b[31m";
pub const ANSI_GREEN: &str = "\x1b[32m";
pub const ANSI_YELLOW: &str = "\x1b[33m";
pub const ANSI_CYAN: &str = "\x1b[36m";
#[must_use]
pub fn ansi_wrap(color: &str, text: &str, enabled: bool) -> String {
if enabled {
format!("{color}{text}\x1b[0m")
} else {
text.to_owned()
}
}
pub fn write_through_pager<F>(no_pager: bool, produce: F) -> Result<()>
where
F: FnOnce(&mut dyn std::io::Write, bool) -> Result<()>,
{
let stdout_is_tty = std::io::stdout().is_terminal();
if !no_pager && stdout_is_tty {
if let Some(mut child) = spawn_pager() {
let stdin = child.stdin.take().expect("stdin was piped");
let mut buf = std::io::BufWriter::new(stdin);
let render_result = produce(&mut buf, true);
drop(buf);
child.wait().ok();
return swallow_broken_pipe(render_result);
}
}
let mut stdout = std::io::stdout();
let colorize = stdout_is_tty;
swallow_broken_pipe(produce(&mut stdout, colorize))
}
fn spawn_pager() -> Option<std::process::Child> {
let pager = std::env::var_os("PAGER").unwrap_or_else(|| "less".into());
let mut cmd = Command::new(&pager);
cmd.stdin(Stdio::piped());
if std::env::var_os("LESS").is_none() {
cmd.env("LESS", "FRX");
}
cmd.spawn().ok()
}
fn swallow_broken_pipe(result: Result<()>) -> Result<()> {
match result {
Ok(()) => Ok(()),
Err(err) => {
let is_broken_pipe = err
.chain()
.filter_map(|cause| cause.downcast_ref::<std::io::Error>())
.any(|io| io.kind() == std::io::ErrorKind::BrokenPipe);
if is_broken_pipe {
Ok(())
} else {
Err(err)
}
}
}
}