use std::path::Path;
use std::process::Stdio;
use std::sync::mpsc;
use std::thread;
use super::chunk::pump;
use super::{Chunk, Cli, CliError, Stream};
impl Cli {
pub fn run(&self, args: &[&str]) -> Result<Stream, CliError> {
self.run_streaming(None, &[], args, None)
}
pub fn run_in(&self, dir: &Path, args: &[&str]) -> Result<Stream, CliError> {
self.run_streaming(Some(dir), &[], args, None)
}
pub fn run_env(&self, env: &[(&str, &str)], args: &[&str]) -> Result<Stream, CliError> {
self.run_streaming(None, env, args, None)
}
pub(super) fn run_streaming(
&self,
cwd: Option<&Path>,
env: &[(&str, &str)],
args: &[&str],
stdin: Option<&[u8]>,
) -> Result<Stream, CliError> {
let mut cmd = self.spawn_base();
cmd.args(args)
.envs(self.standing_env())
.envs(env.iter().copied())
.stdin(match stdin {
Some(_) => Stdio::piped(),
None => Stdio::null(),
})
.stdout(Stdio::piped())
.stderr(Stdio::piped());
if let Some(dir) = cwd {
cmd.current_dir(dir);
}
let mut child = crate::git_env::spawn(&mut cmd)
.map_err(|e| CliError::spawn(self.exec_target(), cwd, e))?;
if let (Some(bytes), Some(mut pipe)) = (stdin, child.stdin.take()) {
let _ = std::io::Write::write_all(&mut pipe, bytes);
}
let stdout = child.stdout.take().ok_or(CliError::Stdio("stdout"))?;
let stderr = child.stderr.take().ok_or(CliError::Stdio("stderr"))?;
let (tx, rx) = mpsc::channel();
let tx_err = tx.clone();
thread::spawn(move || pump(stdout, tx, Chunk::Stdout));
thread::spawn(move || pump(stderr, tx_err, Chunk::Stderr));
Ok(Stream::new(child, rx))
}
pub(super) fn standing_env(&self) -> impl Iterator<Item = (&str, &str)> {
self.env.iter().map(|(k, v)| (k.as_str(), v.as_str()))
}
}