use std::ffi::OsString;
use std::process::Command;
use crate::context::{ColorMode, Context, LogLevel};
use crate::error::{Error, Result};
use crate::output::OutputMode;
fn locate_dispatcher() -> Result<std::path::PathBuf> {
if let Some(explicit) = std::env::var_os("READY_SET_BIN") {
return Ok(std::path::PathBuf::from(explicit));
}
which::which("ready-set").map_err(|_| Error::MissingDependency {
name: "ready-set".to_string(),
hint: Some("install via `cargo install ready-set`".to_string()),
})
}
#[derive(Debug)]
#[non_exhaustive]
pub enum DispatchOutcome {
Streamed {
exit_code: i32,
},
Captured {
stdout: Vec<u8>,
exit_code: i32,
},
}
pub struct DispatchBuilder {
subcommand: String,
args: Vec<OsString>,
capture: bool,
}
impl DispatchBuilder {
#[must_use]
pub fn new(subcommand: impl Into<String>) -> Self {
Self {
subcommand: subcommand.into(),
args: Vec::new(),
capture: false,
}
}
#[must_use]
pub fn arg(mut self, arg: impl Into<OsString>) -> Self {
self.args.push(arg.into());
self
}
#[must_use]
pub fn args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<OsString>,
{
self.args.extend(args.into_iter().map(Into::into));
self
}
#[must_use]
pub const fn capture(mut self, yes: bool) -> Self {
self.capture = yes;
self
}
pub fn run(self, ctx: &Context) -> Result<DispatchOutcome> {
let bin = locate_dispatcher()?;
let mut cmd = Command::new(bin);
cmd.arg(&self.subcommand);
cmd.args(&self.args);
export_contract(&mut cmd, ctx);
if self.capture {
cmd.stdout(std::process::Stdio::piped());
let output = cmd.output()?;
Ok(DispatchOutcome::Captured {
stdout: output.stdout,
exit_code: output.status.code().unwrap_or(-1),
})
} else {
let status = cmd.status()?;
Ok(DispatchOutcome::Streamed {
exit_code: status.code().unwrap_or(-1),
})
}
}
}
pub fn export_contract(cmd: &mut Command, ctx: &Context) {
if let Some(version) = ctx.dispatcher_version() {
cmd.env("READY_SET_DISPATCHER_VERSION", version.to_string());
}
if let Some(root) = ctx.project_root() {
cmd.env("READY_SET_PROJECT_ROOT", root);
}
if let Some(cfg) = ctx.config_path() {
cmd.env("READY_SET_CONFIG_PATH", cfg);
}
cmd.env(
"READY_SET_OUTPUT",
match ctx.output_mode() {
OutputMode::Human => "human",
OutputMode::Json => "json",
},
);
cmd.env(
"READY_SET_LOG",
match ctx.log_level() {
LogLevel::Quiet => "quiet",
LogLevel::Normal => "normal",
LogLevel::Verbose => "verbose",
},
);
cmd.env(
"READY_SET_COLOR",
match ctx.color() {
ColorMode::Auto => "auto",
ColorMode::Always => "always",
ColorMode::Never => "never",
},
);
}