subx_cli/config/
source.rs1use crate::config::manager::ConfigError;
4use crate::config::partial::PartialConfig;
5use log::debug;
6use std::path::PathBuf;
7
8pub trait ConfigSource: Send + Sync {
10 fn load(&self) -> Result<PartialConfig, ConfigError>;
12 fn priority(&self) -> u8;
14 fn source_name(&self) -> &'static str;
16 fn watch_paths(&self) -> Vec<PathBuf> {
18 Vec::new()
19 }
20}
21
22pub struct FileSource {
24 path: PathBuf,
25}
26
27impl FileSource {
28 pub fn new(path: PathBuf) -> Self {
30 Self { path }
31 }
32}
33
34impl ConfigSource for FileSource {
35 fn load(&self) -> Result<PartialConfig, ConfigError> {
36 debug!("FileSource: Attempting to load from path: {:?}", self.path);
37 debug!("FileSource: Path exists: {}", self.path.exists());
38
39 if !self.path.exists() {
40 debug!("FileSource: Path does not exist, returning default config");
41 return Ok(PartialConfig::default());
42 }
43
44 let content = std::fs::read_to_string(&self.path).map_err(|e| {
45 debug!("FileSource: Failed to read file: {}", e);
46 e
47 })?;
48 debug!("FileSource: Read {} bytes from file", content.len());
49 debug!("FileSource: File content:\n{}", content);
50
51 let cfg: PartialConfig = toml::from_str(&content).map_err(|e| {
52 debug!("FileSource: TOML parsing failed: {}", e);
53 ConfigError::ParseError(e.to_string())
54 })?;
55
56 debug!("FileSource: Parsed successfully");
57 debug!(
58 "FileSource: cfg.ai.max_sample_length = {:?}",
59 cfg.ai.max_sample_length
60 );
61 debug!("FileSource: cfg.ai.model = {:?}", cfg.ai.model);
62 debug!("FileSource: cfg.ai.provider = {:?}", cfg.ai.provider);
63
64 Ok(cfg)
65 }
66
67 fn priority(&self) -> u8 {
68 10
69 }
70
71 fn source_name(&self) -> &'static str {
72 "file"
73 }
74 fn watch_paths(&self) -> Vec<PathBuf> {
75 vec![self.path.clone()]
76 }
77}
78
79pub struct EnvSource;
81
82impl EnvSource {
83 pub fn new() -> Self {
84 Self
85 }
86}
87
88impl ConfigSource for EnvSource {
89 fn load(&self) -> Result<PartialConfig, ConfigError> {
90 let mut config = PartialConfig::default();
91 if let Ok(api_key) = std::env::var("OPENAI_API_KEY") {
92 config.ai.api_key = Some(api_key);
93 }
94 if let Ok(model) = std::env::var("SUBX_AI_MODEL") {
95 config.ai.model = Some(model);
96 }
97 if let Ok(provider) = std::env::var("SUBX_AI_PROVIDER") {
98 config.ai.provider = Some(provider);
99 }
100 if let Ok(base_url) = std::env::var("OPENAI_BASE_URL") {
101 config.ai.base_url = Some(base_url);
102 }
103 if let Ok(backup) = std::env::var("SUBX_BACKUP_ENABLED") {
104 config.general.backup_enabled = Some(backup.parse().unwrap_or(false));
105 }
106 Ok(config)
107 }
108
109 fn priority(&self) -> u8 {
110 5 }
112
113 fn source_name(&self) -> &'static str {
114 "environment"
115 }
116}
117
118pub struct CliSource;
120
121impl CliSource {
122 pub fn new() -> Self {
123 Self {}
124 }
125}
126
127impl ConfigSource for CliSource {
128 fn load(&self) -> Result<PartialConfig, ConfigError> {
129 Ok(PartialConfig::default())
130 }
131
132 fn priority(&self) -> u8 {
133 1 }
135
136 fn source_name(&self) -> &'static str {
137 "cli"
138 }
139}
140
141impl Default for EnvSource {
142 fn default() -> Self {
143 EnvSource::new()
144 }
145}
146
147impl Default for CliSource {
148 fn default() -> Self {
149 CliSource::new()
150 }
151}