tomq 0.1.2

jq, but from TOML
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 => {
                // Inner process closed, try to wait for it to exit or just ignore
                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))
    }
}