mod schema;
mod validate;
pub use schema::*;
pub use validate::ValidateError;
pub const STATE_DIR_ENV: &str = "LLMENV_STATE_DIR";
pub const RESERVED_STATE_ENV_VARS: &[&str] = &[STATE_DIR_ENV, "CLAUDE_CONFIG_DIR"];
pub const MEMORY_MCP_NAME: &str = "icm";
use anyhow::Context;
use std::path::Path;
impl Config {
pub fn load(path: &Path) -> anyhow::Result<Self> {
debug_assert!(
!path.starts_with("~"),
"Config::load expects an expanded path; got tilde-prefixed {}",
path.display()
);
let s = std::fs::read_to_string(path)
.with_context(|| format!("failed to read config file: {}", path.display()))?;
let cfg: Self = serde_yaml::from_str(&s)
.with_context(|| format!("failed to parse config file: {}", path.display()))?;
cfg.validate()
.with_context(|| format!("config validation failed: {}", path.display()))?;
Ok(cfg)
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
mod tests {
use super::*;
#[test]
fn load_accepts_expanded_path() {
let tmp = tempfile::tempdir().unwrap();
let p = tmp.path().join("config.yaml");
std::fs::write(&p, "cache: {}\n").unwrap();
assert!(Config::load(&p).is_ok());
}
#[test]
#[should_panic(expected = "expanded path")]
fn load_rejects_tilde_path_in_debug() {
let _ = Config::load(Path::new("~/.config/llmenv/config.yaml"));
}
}