use std::path::Path;
use crate::doctor::check::CheckResult;
use super::Ctx;
const ID: &str = "config.home";
pub fn check(ctx: &Ctx) -> Vec<CheckResult> {
let custom = std::env::var_os("ORCHESTRATECTL_HOME");
let Some(root) = ctx.root.as_deref() else {
return vec![CheckResult::fail(
ID,
"cannot resolve orchestratectl home: neither ORCHESTRATECTL_HOME nor HOME is set",
"set HOME, or export ORCHESTRATECTL_HOME=<dir>",
)];
};
let is_custom = custom.as_ref().is_some_and(|v| !v.is_empty());
if !root.exists() {
return vec![if is_custom {
CheckResult::warn(
ID,
format!(
"ORCHESTRATECTL_HOME points at non-existent path {}",
root.display()
),
format!("create {} or unset ORCHESTRATECTL_HOME", root.display()),
)
} else {
CheckResult::ok(
ID,
format!(
"{} absent (empty state; created on first use)",
root.display()
),
)
}];
}
if !root.is_dir() {
return vec![CheckResult::fail(
ID,
format!("orchestratectl home {} is not a directory", root.display()),
format!(
"remove {} or point ORCHESTRATECTL_HOME elsewhere",
root.display()
),
)];
}
if !is_writable(root) {
return vec![CheckResult::fail(
ID,
format!("orchestratectl home {} is not writable", root.display()),
format!(
"chmod u+w {} or point ORCHESTRATECTL_HOME elsewhere",
root.display()
),
)];
}
vec![CheckResult::ok(
ID,
format!("{} is a writable directory", root.display()),
)]
}
#[cfg(unix)]
fn is_writable(dir: &Path) -> bool {
use std::os::unix::ffi::OsStrExt;
let Ok(c_path) = std::ffi::CString::new(dir.as_os_str().as_bytes()) else {
return false;
};
unsafe { libc::access(c_path.as_ptr(), libc::W_OK) == 0 }
}
#[cfg(not(unix))]
fn is_writable(dir: &Path) -> bool {
match std::fs::metadata(dir) {
Ok(meta) => !meta.permissions().readonly(),
Err(_) => false,
}
}