doum_cli/system/
config.rs1use crate::llm::Provider;
2use crate::system::paths::get_config_path;
3use anyhow::{Context, Result};
4use serde::{Deserialize, Serialize};
5use std::fs;
6use std::path::PathBuf;
7
8#[cfg(unix)]
9use std::os::unix::fs::PermissionsExt;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct Config {
14 pub llm: LLMConfig,
15 pub context: ContextConfig,
16 pub logging: LoggingConfig,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct LLMConfig {
22 pub provider: Provider,
23 pub model: String,
24 pub timeout: u64,
25 pub use_thinking: bool,
26 pub use_web_search: bool,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31pub struct ContextConfig {
32 pub max_lines: usize,
33 pub max_size_kb: usize,
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize)]
38pub struct LoggingConfig {
39 pub enabled: bool,
40 pub level: String,
41}
42
43fn ensure_config() -> Result<PathBuf> {
45 let config_path = get_config_path()?;
46
47 if let Some(parent) = config_path.parent()
48 && !parent.exists()
49 {
50 fs::create_dir_all(parent).context("Failed to create config directory")?;
51
52 #[cfg(unix)]
54 {
55 let metadata = fs::metadata(parent).context("Failed to read directory metadata")?;
56 let mut permissions = metadata.permissions();
57 permissions.set_mode(0o700);
58 fs::set_permissions(parent, permissions)
59 .context("Failed to set directory permissions")?;
60 }
61 }
62
63 Ok(config_path)
64}
65
66pub fn load_config() -> Result<Config> {
68 let config_path = ensure_config()?;
69
70 if config_path.exists() {
71 let content = fs::read_to_string(&config_path).context("Failed to read config file")?;
73 let config: Config = toml::from_str(&content).context("Failed to parse config file")?;
74 Ok(config)
75 } else {
76 let config = load_default_config()?;
78 save_config(&config)?;
79 Ok(config)
80 }
81}
82
83pub fn load_default_config() -> Result<Config> {
85 Ok(Config {
86 llm: LLMConfig {
87 provider: Provider::OpenAI,
88 model: "gpt-5".to_string(),
89 timeout: 30,
90 use_thinking: false,
91 use_web_search: true,
92 },
93 context: ContextConfig {
94 max_lines: 100,
95 max_size_kb: 50,
96 },
97 logging: LoggingConfig {
98 enabled: true,
99 level: "info".to_string(),
100 },
101 })
102}
103
104pub fn save_config(config: &Config) -> Result<()> {
106 let config_path = ensure_config()?;
108 let content = toml::to_string_pretty(config).context("Failed to serialize config")?;
109 fs::write(&config_path, content).context("Failed to write config file")?;
110
111 #[cfg(windows)]
113 {
114 }
117
118 #[cfg(unix)]
120 {
121 let metadata = fs::metadata(&config_path).context("File metadata read failed")?;
122 let mut permissions = metadata.permissions();
123 permissions.set_mode(0o600);
124 fs::set_permissions(&config_path, permissions).context("Failed to set file permissions")?;
125 }
126
127 Ok(())
128}