use std::ffi::OsStr;
use std::path::Path;
use std::process::{Command, CommandArgs, CommandEnvs, ExitStatus, Output};
#[derive(Debug, Copy, Clone)]
pub enum BehaviorOnFailure {
Exit,
DelayFail,
Ignore,
}
#[derive(Debug, Copy, Clone)]
pub enum OutputMode {
All,
OnlyOutput,
OnlyOnFailure,
}
#[derive(Debug)]
pub struct BootstrapCommand {
pub command: Command,
pub failure_behavior: BehaviorOnFailure,
pub output_mode: Option<OutputMode>,
}
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
}
pub fn delay_failure(self) -> Self {
Self { failure_behavior: BehaviorOnFailure::DelayFail, ..self }
}
pub fn fail_fast(self) -> Self {
Self { failure_behavior: BehaviorOnFailure::Exit, ..self }
}
pub fn allow_failure(self) -> Self {
Self { failure_behavior: BehaviorOnFailure::Ignore, ..self }
}
pub fn quiet(self) -> Self {
self.output_mode(OutputMode::OnlyOnFailure)
}
pub fn output_mode(self, output_mode: OutputMode) -> Self {
Self { output_mode: Some(output_mode), ..self }
}
}
impl<'a> From<&'a mut Command> for BootstrapCommand {
fn from(command: &'a mut Command) -> Self {
let mut cmd = Command::new(command.get_program());
if let Some(dir) = command.get_current_dir() {
cmd.current_dir(dir);
}
cmd.args(command.get_args());
for (key, value) in command.get_envs() {
match value {
Some(value) => {
cmd.env(key, value);
}
None => {
cmd.env_remove(key);
}
}
}
cmd.into()
}
}
impl<'a> From<&'a mut BootstrapCommand> for BootstrapCommand {
fn from(command: &'a mut BootstrapCommand) -> Self {
BootstrapCommand::from(&mut command.command)
}
}
impl From<Command> for BootstrapCommand {
fn from(command: Command) -> Self {
Self { command, failure_behavior: BehaviorOnFailure::Exit, output_mode: None }
}
}
#[allow(unused)]
pub struct CommandOutput(Output);
impl CommandOutput {
pub fn is_success(&self) -> bool {
self.0.status.success()
}
pub fn is_failure(&self) -> bool {
!self.is_success()
}
pub fn status(&self) -> ExitStatus {
self.0.status
}
pub fn stdout(&self) -> String {
String::from_utf8(self.0.stdout.clone()).expect("Cannot parse process stdout as UTF-8")
}
pub fn stderr(&self) -> String {
String::from_utf8(self.0.stderr.clone()).expect("Cannot parse process stderr as UTF-8")
}
}
impl Default for CommandOutput {
fn default() -> Self {
Self(Output { status: Default::default(), stdout: vec![], stderr: vec![] })
}
}
impl From<Output> for CommandOutput {
fn from(output: Output) -> Self {
Self(output)
}
}
impl From<ExitStatus> for CommandOutput {
fn from(status: ExitStatus) -> Self {
Self(Output { status, stdout: vec![], stderr: vec![] })
}
}