use std::env;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
pub(crate) type AnyError = Box<dyn Error>;
pub(crate) type Result<T> = std::result::Result<T, AnyError>;
pub(crate) fn usage_error<T>(message: String) -> Result<T> {
Err(message.into())
}
pub(crate) fn repo_root() -> PathBuf {
if let Ok(root) = env::var("EXECUTESOFT_ROOT") {
return PathBuf::from(root);
}
let mut dir = env::current_dir().unwrap_or_else(|_| PathBuf::from("."));
loop {
if dir.join("contracts/protobuf").exists() && dir.join("services").exists() {
return dir;
}
if !dir.pop() {
return PathBuf::from(".");
}
}
}
pub(crate) fn repo_path(path: &str) -> PathBuf {
let raw = PathBuf::from(path);
if raw.is_absolute() || raw.exists() {
return raw;
}
let from_root = repo_root().join(path);
if from_root.exists() {
return from_root;
}
raw
}
pub(crate) fn run_cmd(dir: &Path, program: &str, args: &[String]) -> Result<()> {
let status = Command::new(program)
.args(args)
.current_dir(dir)
.stdin(Stdio::inherit())
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.status()?;
if status.success() {
Ok(())
} else {
Err(format!("{program} exited with {status}").into())
}
}
pub(crate) fn run_make(dir: &Path, target: &str, vars: &[String]) -> Result<()> {
let mut args = vec![target.to_string()];
args.extend(vars.iter().cloned());
run_cmd(dir, "make", &args)
}
pub(crate) fn make_vars(args: &[String]) -> Vec<String> {
let mut vars = Vec::new();
for arg in args {
if let Some(raw) = arg.strip_prefix("--") {
if let Some((key, value)) = raw.split_once('=') {
vars.push(format!(
"{}={}",
key.replace('-', "_").to_uppercase(),
value
));
}
} else if arg.contains('=') {
vars.push(arg.clone());
}
}
vars
}