use super::child;
use serde::Deserialize;
use std::io::{self, Read, Write};
use std::process::Command;
use std::sync::atomic::AtomicBool;
use std::time::Duration;
use thiserror::Error;
#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
struct Input {
command: String,
}
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid input JSON: {0}")]
InvalidJson(#[source] serde_json::Error),
#[error("read input from stdin: {0}")]
StdinRead(#[source] io::Error),
#[error(transparent)]
Child(#[from] child::Error),
#[error("write to stdout: {0}")]
Stdout(#[source] io::Error),
#[error("write to stderr: {0}")]
Stderr(#[source] io::Error),
}
#[rustfmt::skip]
pub fn run<R: Read, W: Write, E: Write>(
stdin: &mut R, stdout: &mut W, stderr: &mut E,
) -> Result<i32, Error> {
child::install_sigterm_handler();
run_with(stdin, stdout, stderr, "sh", child::sigterm_flag(), child::CASCADE_DEADLINE)
}
#[doc(hidden)]
pub(crate) fn run_with<R: Read, W: Write, E: Write>(
stdin: &mut R,
stdout: &mut W,
stderr: &mut E,
shell: &str,
stop: &AtomicBool,
deadline: Duration,
) -> Result<i32, Error> {
let mut buf = Vec::new();
stdin.read_to_end(&mut buf).map_err(Error::StdinRead)?;
let input: Input = serde_json::from_slice(&buf).map_err(Error::InvalidJson)?;
let mut cmd = Command::new(shell);
cmd.arg("-c").arg(&input.command);
let done = child::run(&mut cmd, None, stop, deadline)?;
stdout.write_all(&done.stdout).map_err(Error::Stdout)?;
stderr.write_all(&done.stderr).map_err(Error::Stderr)?;
Ok(done.code)
}
#[cfg(test)]
mod tests;