use super::Config;
use crate::diagnostic::{Diagnostic, DiagnosticCode, DiagnosticResult};
use std::path::{Path, PathBuf};
impl Config {
pub fn load(path: Option<&Path>) -> DiagnosticResult<Self> {
let config_path = path
.map(PathBuf::from)
.or_else(Self::find_config)
.unwrap_or_else(|| PathBuf::from("gov/config.toml"));
if config_path.exists() {
let content = std::fs::read_to_string(&config_path).map_err(|err| {
Diagnostic::io_error("read config", err, config_path.display().to_string())
})?;
let mut config: Config = toml::from_str(&content).map_err(|err| {
Diagnostic::new(
DiagnosticCode::E0501ConfigInvalid,
format!("Failed to parse config: {err}"),
config_path.display().to_string(),
)
})?;
if let Some(project_root) = config_path.parent().and_then(|p| p.parent()) {
config.gov_root = project_root.join("gov");
if config.paths.docs_output.is_relative() {
config.paths.docs_output = project_root.join(&config.paths.docs_output);
}
if config.paths.agent_dir.is_relative() {
config.paths.agent_dir = project_root.join(&config.paths.agent_dir);
}
}
Ok(config)
} else {
Ok(Config::default())
}
}
fn find_config() -> Option<PathBuf> {
let mut current = std::env::current_dir().ok()?;
loop {
let config_path = current.join("gov/config.toml");
if config_path.exists() {
return Some(config_path);
}
if !current.pop() {
return None;
}
}
}
pub fn rfc_dir(&self) -> PathBuf {
self.gov_root.join("rfc")
}
pub fn rfc_artifact_dir(&self, rfc_id: &str) -> PathBuf {
self.rfc_dir().join(rfc_id)
}
pub fn rfc_source_path(&self, rfc_id: &str, extension: &str) -> PathBuf {
self.rfc_artifact_dir(rfc_id)
.join(format!("rfc.{extension}"))
}
pub fn clause_dir(&self, rfc_id: &str) -> PathBuf {
self.rfc_artifact_dir(rfc_id).join("clauses")
}
pub fn clause_source_path(&self, rfc_id: &str, clause_name: &str, extension: &str) -> PathBuf {
self.clause_dir(rfc_id)
.join(format!("{clause_name}.{extension}"))
}
pub fn adr_dir(&self) -> PathBuf {
self.gov_root.join("adr")
}
pub fn work_dir(&self) -> PathBuf {
self.gov_root.join("work")
}
pub fn schema_dir(&self) -> PathBuf {
self.gov_root.join("schema")
}
pub fn guard_dir(&self) -> PathBuf {
self.gov_root.join("guard")
}
pub fn templates_dir(&self) -> PathBuf {
self.gov_root.join("templates")
}
pub fn rfc_output(&self) -> PathBuf {
self.paths.docs_output.join("rfc")
}
pub fn adr_output(&self) -> PathBuf {
self.paths.docs_output.join("adr")
}
pub fn work_output(&self) -> PathBuf {
self.paths.docs_output.join("work")
}
pub fn releases_path(&self) -> PathBuf {
self.gov_root.join("releases.toml")
}
pub fn display_path(&self, path: &Path) -> PathBuf {
self.gov_root
.parent()
.and_then(|root| path.strip_prefix(root).ok())
.map(PathBuf::from)
.unwrap_or_else(|| path.to_path_buf())
}
}