use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::error::CliError;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct TelemetryConfig {
#[serde(default)]
pub enabled: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct Config {
pub default_output: Option<String>,
pub default_force: Option<bool>,
pub plugin_version: Option<String>,
pub template_version: Option<String>,
#[serde(default)]
pub telemetry: TelemetryConfig,
}
impl Config {
pub fn load(config_file: Option<&PathBuf>) -> Self {
if let Some(path) = config_file {
return Self::load_from_path(path).unwrap_or_else(|e| {
tracing::warn!("failed to load config from {}: {e}", path.display());
Self::default()
});
}
if let Some(path) = Self::default_config_path() {
if path.exists() {
return Self::load_from_path(&path).unwrap_or_else(|e| {
tracing::warn!("failed to load default config: {e}");
Self::default()
});
}
}
Self::default()
}
fn load_from_path(path: &PathBuf) -> Result<Self, CliError> {
let content = std::fs::read_to_string(path)
.map_err(|e| CliError::io(path.to_string_lossy().into_owned(), e))?;
toml::from_str(&content)
.map_err(|e| CliError::ConfigParse(format!("invalid configuration: {e}")))
}
pub fn default_config_path() -> Option<PathBuf> {
directories::ProjectDirs::from("", "", "opencode-ralph-loop-cli")
.map(|d| d.config_dir().join("config.toml"))
}
pub fn config_path_display() -> String {
Self::default_config_path()
.map(|p| p.display().to_string())
.unwrap_or_else(|| "unavailable".to_string())
}
}