use std::env;
use std::fs;
use std::path::Path;
use std::process::{self, Command};
pub fn log(msg: &str) {
eprintln!("\x1b[32m[devforge]\x1b[0m {msg}");
}
pub fn fatal(msg: &str) -> ! {
eprintln!("\x1b[31m[devforge]\x1b[0m {msg}");
process::exit(1);
}
pub fn check_file(path: &Path) {
if !path.exists() {
fatal(&format!("Required file not found: {}", path.display()));
}
}
pub fn require_cmd(name: &str) {
let found = Command::new("which")
.arg(name)
.output()
.map(|o| o.status.success())
.unwrap_or(false);
if !found {
fatal(&format!("Required command not found: {name}"));
}
}
pub fn load_dotenv(path: &Path) {
let contents = match fs::read_to_string(path) {
Ok(c) => c,
Err(e) => fatal(&format!("Failed to read {}: {e}", path.display())),
};
for line in contents.lines() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') {
continue;
}
if let Some((key, value)) = line.split_once('=') {
let key = key.trim();
let value = value.trim().trim_matches('"').trim_matches('\'');
unsafe { env::set_var(key, value) };
}
}
}
pub fn workspace_root() -> std::path::PathBuf {
let start = env::var("CARGO_MANIFEST_DIR")
.map(std::path::PathBuf::from)
.unwrap_or_else(|_| env::current_dir().expect("cannot determine current directory"));
let mut dir = start.as_path();
loop {
if dir.join("devforge.toml").exists() {
return dir.to_path_buf();
}
match dir.parent() {
Some(parent) => dir = parent,
None => fatal("Could not find devforge.toml in any parent directory"),
}
}
}