use std::io::Read;
use std::process::{Child, ChildStdout, Command, Stdio};

pub struct SubprocessReader {
    name: String,
    subprocess: Child,
    stdout: ChildStdout,
}

impl SubprocessReader {
    pub fn from_command(mut command: Command, name: String) -> std::io::Result<SubprocessReader> {
        let subprocess = command
            .stdin(Stdio::null())
            .stdout(Stdio::piped())
            .stderr(Stdio::null())
            .spawn()?;
        Self::from_child(subprocess, name)
    }

    pub fn from_child(mut subprocess: Child, name: String) -> std::io::Result<SubprocessReader> {
        let stdout = if let Some(stdout) = subprocess.stdout.take() {
            stdout
        } else {
            return Err(std::io::Error::new(
                std::io::ErrorKind::Other,
                format!("stdout missing on child subprocess for {}", name),
            ));
        };

        Ok(SubprocessReader {
            name,
            subprocess,
            stdout,
        })
    }
}

impl Read for SubprocessReader {
    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
        match self.stdout.read(buf) {
            Ok(n) => {
                if n != 0 {
                    Ok(n)
                } else {
                    match self.subprocess.wait() {
                        Ok(exit_status) => {
                            if exit_status.success() {
                                Ok(0)
                            } else {
                                Err(std::io::Error::new(
                                    std::io::ErrorKind::Other,
                                    format!(
                                        "subprocess for {} exited with error: {}",
                                        self.name, exit_status
                                    ),
                                ))
                            }
                        }
                        Err(e) => Err(e),
                    }
                }
            }
            Err(e) => {
                self.subprocess.wait()?;
                Err(e)
            }
        }
    }
}
