use std::env;
use std::fmt;
use std::io::{self, Write};
use std::process::{Child, Command, Stdio};
use std::sync::LazyLock;
pub static PAGER: LazyLock<String> = LazyLock::new(|| {
env::var("PAGER").unwrap_or_else(|_| {
if cfg!(windows) {
#[cfg(not(tarpaulin_include))]
String::from("more.com")
} else {
String::from("less")
}
})
});
pub struct Pager;
impl Pager {
pub fn page_or_print(content: &str) {
if Self::page(content).is_err() {
let mut stdout = std::io::stdout();
if content.ends_with('\n') {
let _ = write!(stdout, "{content}");
} else {
let _ = writeln!(stdout, "{content}");
}
}
}
pub fn page(content: &str) -> Result<(), io::Error> {
let mut child = match spawn_pager(&PAGER, &[]) {
Err(e)
if e.kind() == io::ErrorKind::NotFound && PAGER.contains(char::is_whitespace) =>
{
let mut parts = PAGER.split_whitespace();
let program = parts.next().unwrap_or_default();
spawn_pager(program, &parts.collect::<Vec<_>>())?
}
result => result?,
};
let Some(stdin) = child.stdin.as_mut() else {
#[cfg(not(tarpaulin_include))]
return Err(io::Error::new(
io::ErrorKind::BrokenPipe,
"Failed to open stdin.",
));
};
if content.ends_with('\n') {
write!(stdin, "{content}")?;
} else {
writeln!(stdin, "{content}")?;
}
child.wait()?;
Ok(())
}
}
#[cfg(not(tarpaulin_include))]
pub trait OutputPaged {
fn output_paged(&self) {}
}
impl<T> OutputPaged for T
where
T: fmt::Display,
{
fn output_paged(&self) {
Pager::page_or_print(&self.to_string());
}
}
fn spawn_pager(program: &str, args: &[&str]) -> io::Result<Child> {
let mut pager = Command::new(program);
#[cfg(not(tarpaulin_include))]
{
if program == "less" || program.ends_with("/less") {
pager.env("LESSCHARSET", "UTF-8");
pager.arg("-R"); pager.arg("-F"); pager.arg("-X"); }
}
pager.args(args);
pager.stdin(Stdio::piped());
pager.stdout(Stdio::inherit());
pager.stderr(Stdio::inherit());
pager.spawn()
}