radicle_cli/
pager.rs

1use std::io;
2use std::process::{Command, Stdio};
3
4use radicle_term::element;
5use radicle_term::{Constraint, Element};
6
7use crate::terminal;
8
9/// Output the given element through a pager, if necessary.
10/// If it fits within the screen, don't run it through a pager.
11pub fn run(elem: impl Element) -> io::Result<()> {
12    let Some(constraint) = Constraint::from_env() else {
13        return elem.write(Constraint::UNBOUNDED);
14    };
15    let Some(rows) = terminal::rows() else {
16        return elem.write(Constraint::UNBOUNDED);
17    };
18    if elem.size(Constraint::UNBOUNDED).rows <= rows {
19        return elem.write(Constraint::UNBOUNDED);
20    }
21    let Some(pager) = radicle::profile::env::pager() else {
22        return elem.write(Constraint::UNBOUNDED);
23    };
24    let Some(parts) = shlex::split(&pager) else {
25        return elem.write(Constraint::UNBOUNDED);
26    };
27    let Some((program, args)) = parts.split_first() else {
28        return elem.write(Constraint::UNBOUNDED);
29    };
30
31    let mut child = Command::new(program)
32        .stdin(Stdio::piped())
33        .stdout(Stdio::inherit())
34        .stderr(Stdio::inherit())
35        .args(args)
36        .spawn()?;
37
38    let writer = child.stdin.as_mut().unwrap();
39    let result = element::write_to(&elem, writer, constraint);
40
41    child.wait()?;
42
43    match result {
44        // This error is expected when the pager is exited.
45        Err(e) if e.kind() == io::ErrorKind::BrokenPipe => {}
46        Err(e) => return Err(e),
47        Ok(_) => {}
48    }
49
50    Ok(())
51}