use crate::bin_finder::find_bin;
use crate::output::OutputWriter;
use std::io::{Error, ErrorKind, Read};
use std::io::{Result, Write};
use std::path::PathBuf;
use std::process::{Child, ChildStdin, Command, Stdio};
pub(crate) struct PagerHandle {
proc: Child,
stdin: ChildStdin,
}
pub(crate) struct Bat;
impl Bat {
pub(crate) fn new_handle(language: &str) -> Result<PagerHandle> {
let mut command = Command::new(find_bin("bat").unwrap_or(PathBuf::from("bat")));
let mut proc = {
if let Ok(v) = std::env::var("LESS") {
command.env("LESS", v);
};
command.args(&["--paging", "always", "--language", language])
}
.stdin(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
let stdin = proc.stdin.take().ok_or_else(|| {
Error::new(ErrorKind::Other, "could not get stdin from spawned process")
})?;
Ok(PagerHandle { proc, stdin })
}
}
impl PagerHandle {
pub(crate) fn into_writer(self) -> PagerHandleWriter {
PagerHandleWriter { handle: self }
}
pub(crate) fn handle_read(self, mut msg: impl Read) -> Result<i32> {
let mut proc = self.proc;
let mut child_stdin = self.stdin;
match std::io::copy(&mut msg, &mut child_stdin) {
Ok(_) => {}
Err(e) if e.kind() == ErrorKind::BrokenPipe => {
if let Ok(status) = proc.wait() {
if let Some(code) = status.code() {
return Ok(code);
}
}
return Ok(0);
}
Err(e) => {
if e.kind() == ErrorKind::BrokenPipe {
return Ok(0);
}
}
}
drop(child_stdin);
Ok(proc.wait()?.code().unwrap_or(0))
}
}
pub(crate) struct PagerHandleWriter {
handle: PagerHandle,
}
impl Write for PagerHandleWriter {
fn write(&mut self, buf: &[u8]) -> Result<usize> {
self.handle.stdin.write(buf)
}
fn flush(&mut self) -> Result<()> {
self.handle.stdin.flush()
}
}
impl OutputWriter for PagerHandleWriter {
fn wait(self) -> crate::error::Result<i32> {
let stdin = self.handle.stdin;
let mut proc = self.handle.proc;
drop(stdin);
Ok(proc.wait()?.code().unwrap_or(0))
}
}