opencode_ralph_loop_cli/
config.rs1use std::path::PathBuf;
2
3use serde::{Deserialize, Serialize};
4
5use crate::error::CliError;
6
7#[derive(Debug, Clone, Serialize, Deserialize, Default)]
8pub struct TelemetryConfig {
9 #[serde(default)]
10 pub enabled: bool,
11}
12
13#[derive(Debug, Clone, Serialize, Deserialize, Default)]
14pub struct Config {
15 pub default_output: Option<String>,
16 pub default_force: Option<bool>,
17 pub plugin_version: Option<String>,
18 pub template_version: Option<String>,
19 #[serde(default)]
20 pub telemetry: TelemetryConfig,
21}
22
23impl Config {
24 pub fn load(config_file: Option<&PathBuf>) -> Self {
25 if let Some(path) = config_file {
26 return Self::load_from_path(path).unwrap_or_else(|e| {
27 tracing::warn!("failed to load config from {}: {e}", path.display());
28 Self::default()
29 });
30 }
31
32 if let Some(path) = Self::default_config_path() {
33 if path.exists() {
34 return Self::load_from_path(&path).unwrap_or_else(|e| {
35 tracing::warn!("failed to load default config: {e}");
36 Self::default()
37 });
38 }
39 }
40
41 Self::default()
42 }
43
44 fn load_from_path(path: &PathBuf) -> Result<Self, CliError> {
45 let content = std::fs::read_to_string(path)
46 .map_err(|e| CliError::io(path.to_string_lossy().into_owned(), e))?;
47 toml::from_str(&content)
48 .map_err(|e| CliError::ConfigParse(format!("invalid configuration: {e}")))
49 }
50
51 pub fn default_config_path() -> Option<PathBuf> {
52 directories::ProjectDirs::from("", "", "opencode-ralph-loop-cli")
53 .map(|d| d.config_dir().join("config.toml"))
54 }
55
56 pub fn config_path_display() -> String {
57 Self::default_config_path()
58 .map(|p| p.display().to_string())
59 .unwrap_or_else(|| "unavailable".to_string())
60 }
61}