use crate::Build;
use std::ffi::OsStr;
use std::path::Path;
use std::process::{Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio};
#[derive(Debug, Copy, Clone)]
pub enum BehaviorOnFailure {
Exit,
DelayFail,
Ignore,
}
#[derive(Debug, Copy, Clone)]
pub enum OutputMode {
Print,
Capture,
}
impl OutputMode {
pub fn captures(&self) -> bool {
match self {
OutputMode::Print => false,
OutputMode::Capture => true,
}
}
pub fn stdio(&self) -> Stdio {
match self {
OutputMode::Print => Stdio::inherit(),
OutputMode::Capture => Stdio::piped(),
}
}
}
#[derive(Debug)]
pub struct BootstrapCommand {
pub command: Command,
pub failure_behavior: BehaviorOnFailure,
pub stdout: OutputMode,
pub stderr: OutputMode,
pub run_always: bool,
}
impl BootstrapCommand {
pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
Command::new(program).into()
}
pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Self {
self.command.arg(arg.as_ref());
self
}
pub fn args<I, S>(&mut self, args: I) -> &mut Self
where
I: IntoIterator<Item = S>,
S: AsRef<OsStr>,
{
self.command.args(args);
self
}
pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self
where
K: AsRef<OsStr>,
V: AsRef<OsStr>,
{
self.command.env(key, val);
self
}
pub fn get_envs(&self) -> CommandEnvs<'_> {
self.command.get_envs()
}
pub fn get_args(&self) -> CommandArgs<'_> {
self.command.get_args()
}
pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Self {
self.command.env_remove(key);
self
}
pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Self {
self.command.current_dir(dir);
self
}
#[must_use]
pub fn delay_failure(self) -> Self {
Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self }
}
#[must_use]
pub fn fail_fast(self) -> Self {
Self { failure_behavior: BehaviorOnFailure::Exit, ..self }
}
#[must_use]
pub fn allow_failure(self) -> Self {
Self { failure_behavior: BehaviorOnFailure::Ignore, ..self }
}
pub fn run_always(&mut self) -> &mut Self {
self.run_always = true;
self
}
#[must_use]
pub fn capture(self) -> Self {
Self { stdout: OutputMode::Capture, stderr: OutputMode::Capture, ..self }
}
#[must_use]
pub fn capture_stdout(self) -> Self {
Self { stdout: OutputMode::Capture, ..self }
}
pub fn run(&mut self, builder: &Build) -> CommandOutput {
builder.run(self)
}
}
impl From<Command> for BootstrapCommand {
fn from(command: Command) -> Self {
Self {
command,
failure_behavior: BehaviorOnFailure::Exit,
stdout: OutputMode::Print,
stderr: OutputMode::Print,
run_always: false,
}
}
}
enum CommandStatus {
Finished(ExitStatus),
DidNotStart,
}
#[must_use]
pub fn command<S: AsRef<OsStr>>(program: S) -> BootstrapCommand {
BootstrapCommand::new(program)
}
#[allow(unused)]
pub struct CommandOutput {
status: CommandStatus,
stdout: Vec<u8>,
stderr: Vec<u8>,
}
impl CommandOutput {
#[must_use]
pub fn did_not_start() -> Self {
Self { status: CommandStatus::DidNotStart, stdout: vec![], stderr: vec![] }
}
#[must_use]
pub fn is_success(&self) -> bool {
match self.status {
CommandStatus::Finished(status) => status.success(),
CommandStatus::DidNotStart => false,
}
}
#[must_use]
pub fn is_failure(&self) -> bool {
!self.is_success()
}
#[must_use]
pub fn status(&self) -> Option<ExitStatus> {
match self.status {
CommandStatus::Finished(status) => Some(status),
CommandStatus::DidNotStart => None,
}
}
#[must_use]
pub fn stdout(&self) -> String {
String::from_utf8(self.stdout.clone()).expect("Cannot parse process stdout as UTF-8")
}
#[must_use]
pub fn stdout_if_ok(&self) -> Option<String> {
if self.is_success() { Some(self.stdout()) } else { None }
}
#[must_use]
pub fn stderr(&self) -> String {
String::from_utf8(self.stderr.clone()).expect("Cannot parse process stderr as UTF-8")
}
}
impl Default for CommandOutput {
fn default() -> Self {
Self {
status: CommandStatus::Finished(ExitStatus::default()),
stdout: vec![],
stderr: vec![],
}
}
}
impl From<Output> for CommandOutput {
fn from(output: Output) -> Self {
Self {
status: CommandStatus::Finished(output.status),
stdout: output.stdout,
stderr: output.stderr,
}
}
}