use std::path::PathBuf;
pub const ENV_SUBCMD: &str = "env";
pub const EXEC_SUBCMD: &str = "exec";
pub const CWD_FLAG: &str = "--cwd";
pub fn shell_quote(s: &str) -> String {
let mut out = String::with_capacity(s.len() + 2);
out.push('\'');
for ch in s.chars() {
if ch == '\'' {
out.push_str("'\\''");
} else {
out.push(ch);
}
}
out.push('\'');
out
}
pub fn env_script(overrides: &[(String, String)]) -> String {
let mut out = String::new();
for (key, value) in overrides {
out.push_str("export ");
out.push_str(key);
out.push('=');
out.push_str(&shell_quote(value));
out.push('\n');
}
out
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExecPlan {
pub cwd: Option<PathBuf>,
pub cmd: String,
pub args: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ExecError {
#[error("--cwd requires a directory argument")]
MissingCwdValue,
#[error("no command given (usage: yog exec [--cwd DIR] <cmd> [args...])")]
MissingCommand,
}
pub fn parse_exec(args: &[String]) -> Result<ExecPlan, ExecError> {
let (cwd, rest) = match args.split_first() {
Some((flag, tail)) if flag.as_str() == CWD_FLAG => {
let (dir, more) = tail.split_first().ok_or(ExecError::MissingCwdValue)?;
(Some(PathBuf::from(dir)), more)
}
_ => (None, args),
};
let (cmd, cmd_args) = rest.split_first().ok_or(ExecError::MissingCommand)?;
Ok(ExecPlan {
cwd,
cmd: cmd.clone(),
args: cmd_args.to_vec(),
})
}
#[cfg(test)]
mod tests;