use std::{
fs,
path::{Path, PathBuf},
};
use directories::ProjectDirs;
use log::{debug, info};
use thiserror::Error;
use orrery::{OrreryError, config::AppConfig};
#[derive(Debug, Error)]
pub enum ConfigError {
#[error("Failed to parse TOML configuration: {0}")]
Parse(String),
#[error("Missing configuration file: {0}")]
MissingFile(PathBuf),
#[error("Validation error: {0}")]
Validation(String),
}
impl From<ConfigError> for OrreryError {
fn from(err: ConfigError) -> Self {
OrreryError::Io(std::io::Error::other(err.to_string()))
}
}
pub fn load_config(explicit_path: Option<impl AsRef<Path>>) -> Result<AppConfig, OrreryError> {
if let Some(path) = explicit_path {
let path = path.as_ref();
info!(path = path.display().to_string(); "Loading configuration from explicit path");
return load_config_file(path);
}
let local_config = Path::new("orrery/config.toml");
if local_config.exists() {
info!(path = local_config.display().to_string(); "Loading configuration from local path");
return load_config_file(local_config);
}
if let Some(proj_dirs) = ProjectDirs::from("com", "orrery", "orrery") {
let config_dir = proj_dirs.config_dir();
let system_config = config_dir.join("config.toml");
if system_config.exists() {
info!(path = system_config.display().to_string(); "Loading configuration from system path");
return load_config_file(system_config);
}
debug!(path = system_config.display().to_string(); "System configuration file not found");
} else {
debug!("Could not determine platform-specific config directory");
}
debug!("No configuration file found, using default configuration");
Ok(AppConfig::default())
}
fn load_config_file(path: impl AsRef<Path>) -> Result<AppConfig, OrreryError> {
let path = path.as_ref();
if !path.exists() {
return Err(ConfigError::MissingFile(path.to_path_buf()).into());
}
let content = fs::read_to_string(path)?;
let config: AppConfig =
toml::from_str(&content).map_err(|err| ConfigError::Parse(err.to_string()))?;
Ok(config)
}