use std::io;
use std::path::Path;
use crate::cli_outbound::{Chunk, Cli, CliError, ExitInfo, Stream};
use crate::opslog::{self, OpEntry, Origin};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Outcome {
pub exit: i32,
pub stdout: String,
pub stderr: String,
}
impl Outcome {
pub fn ok(&self) -> bool {
self.exit == 0
}
}
pub(crate) fn collect(stream: Result<Stream, CliError>) -> io::Result<Outcome> {
Ok(drain(stream.map_err(io::Error::other)?))
}
fn drain(stream: Stream) -> Outcome {
let mut out = Vec::new();
let mut err = Vec::new();
let mut exit = ExitInfo::Unknown;
for chunk in stream {
match chunk {
Chunk::Stdout(b) => out.extend(b),
Chunk::Stderr(b) => err.extend(b),
Chunk::Exited(e) => exit = e,
}
}
Outcome {
exit: exit.shell_code(),
stdout: String::from_utf8_lossy(&out).into_owned(),
stderr: String::from_utf8_lossy(&err).into_owned(),
}
}
pub fn run_logged(
cli: &Cli,
state_root: &Path,
ts: &str,
cwd: &Path,
args: &[&str],
origin: Origin,
) -> io::Result<Outcome> {
let stream = cli.run_in(cwd, args);
log_attempt(
cli,
state_root,
ts,
cwd.display().to_string(),
stream,
args,
origin,
)
}
pub fn run_logged_cwdless(
cli: &Cli,
state_root: &Path,
ts: &str,
args: &[&str],
origin: Origin,
) -> io::Result<Outcome> {
let stream = cli.run(args);
log_attempt(cli, state_root, ts, String::new(), stream, args, origin)
}
fn log_attempt(
cli: &Cli,
state_root: &Path,
ts: &str,
cwd: String,
stream: Result<Stream, CliError>,
args: &[&str],
origin: Origin,
) -> io::Result<Outcome> {
let argv = argv_of(cli, args);
match stream {
Ok(stream) => {
let outcome = drain(stream);
opslog::append(
state_root,
&OpEntry {
ts: ts.to_string(),
argv,
cwd,
exit: outcome.exit,
stdout: outcome.stdout.clone(),
stderr: outcome.stderr.clone(),
origin,
client: cli.client(),
},
)?;
Ok(outcome)
}
Err(spawn) => {
let entry = OpEntry::synthetic_failure(
ts.to_string(),
argv,
cwd,
spawn.to_string(),
origin,
cli.client(),
);
opslog::append(state_root, &entry)?;
Err(io::Error::other(spawn))
}
}
}
fn argv_of(cli: &Cli, args: &[&str]) -> Vec<String> {
let mut argv = Vec::with_capacity(args.len() + 1);
argv.push(cli.binary().display().to_string());
argv.extend(args.iter().map(|s| (*s).to_string()));
argv
}
pub fn log_step_done(
state_root: &Path,
ts: &str,
cwd: &Path,
step: &str,
origin: Origin,
client: crate::registry::Client,
) -> io::Result<()> {
opslog::append(
state_root,
&OpEntry::step_done(
ts.to_string(),
step,
cwd.display().to_string(),
origin,
client,
),
)
}
pub fn log_step_failure(
state_root: &Path,
ts: &str,
cwd: &Path,
step: &str,
err: &str,
origin: Origin,
client: crate::registry::Client,
) -> io::Result<()> {
opslog::append(
state_root,
&OpEntry::step_failure(
ts.to_string(),
step,
cwd.display().to_string(),
err.to_string(),
origin,
client,
),
)
}