1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8use std::path::{Path, PathBuf};
9
10use crate::core::sensor::{Band, Sensor, SensorRegistry};
11use crate::error::{GlintError, Result};
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct SensorConfigFile {
16 pub version: String,
18 pub sensors: Vec<SensorConfig>,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct SensorConfig {
25 pub id: String,
27 pub name: String,
29 pub description: Option<String>,
31 pub bit_depth: u8,
33 pub loader_type: String,
35 pub bands: Vec<BandConfig>,
37 pub loader_config: Option<HashMap<String, String>>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
43pub struct BandConfig {
44 pub name: String,
46 pub default_threshold: f64,
48 pub wavelength: Option<f64>,
50 pub description: Option<String>,
52}
53
54impl From<BandConfig> for Band {
55 fn from(config: BandConfig) -> Self {
56 Band {
57 name: config.name,
58 default_threshold: config.default_threshold,
59 wavelength: config.wavelength,
60 description: config.description,
61 }
62 }
63}
64
65impl From<Band> for BandConfig {
66 fn from(band: Band) -> Self {
67 BandConfig {
68 name: band.name,
69 default_threshold: band.default_threshold,
70 wavelength: band.wavelength,
71 description: band.description,
72 }
73 }
74}
75
76impl From<SensorConfig> for Sensor {
77 fn from(config: SensorConfig) -> Self {
78 let bands: Vec<Band> = config.bands.into_iter().map(Band::from).collect();
79 let mut sensor = Sensor::new(
80 config.id,
81 config.name,
82 bands,
83 config.bit_depth,
84 config.loader_type,
85 );
86
87 if let Some(description) = config.description {
88 sensor = sensor.with_description(description);
89 }
90
91 if let Some(loader_config) = config.loader_config {
92 for (key, value) in loader_config {
93 sensor = sensor.with_loader_config(key, value);
94 }
95 }
96
97 sensor
98 }
99}
100
101impl From<Sensor> for SensorConfig {
102 fn from(sensor: Sensor) -> Self {
103 let bands: Vec<BandConfig> = sensor.bands.into_iter().map(BandConfig::from).collect();
104 let loader_config = if sensor.loader_config.is_empty() {
105 None
106 } else {
107 Some(sensor.loader_config)
108 };
109
110 SensorConfig {
111 id: sensor.id,
112 name: sensor.name,
113 description: sensor.description,
114 bit_depth: sensor.bit_depth,
115 loader_type: sensor.loader_type,
116 bands,
117 loader_config,
118 }
119 }
120}
121
122#[derive(Debug)]
124pub struct ConfigManager {
125 config_dir: Option<PathBuf>,
127}
128
129impl ConfigManager {
130 pub fn new() -> Self {
132 Self { config_dir: None }
133 }
134
135 pub fn with_config_dir(config_dir: PathBuf) -> Self {
137 Self {
138 config_dir: Some(config_dir),
139 }
140 }
141
142 pub fn default_config_dir() -> Result<PathBuf> {
144 if let Some(config_dir) = dirs::config_dir() {
146 Ok(config_dir.join("glint-mask-tools"))
147 } else {
148 Ok(PathBuf::from("."))
150 }
151 }
152
153 pub fn get_config_dir(&self) -> Result<PathBuf> {
155 if let Some(ref dir) = self.config_dir {
156 Ok(dir.clone())
157 } else {
158 Self::default_config_dir()
159 }
160 }
161
162 pub fn load_from_file(&self, path: &Path) -> Result<Vec<Sensor>> {
164 let content = std::fs::read_to_string(path).map_err(GlintError::Io)?;
165
166 let config_file: SensorConfigFile =
167 toml::from_str(&content).map_err(GlintError::Deserialization)?;
168
169 if config_file.version != "1.0" {
171 return Err(GlintError::config(format!(
172 "Unsupported configuration file version: {}",
173 config_file.version
174 )));
175 }
176
177 let mut sensors = Vec::new();
179 for sensor_config in config_file.sensors {
180 let sensor = Sensor::from(sensor_config);
181 sensor.validate()?;
182 sensors.push(sensor);
183 }
184
185 Ok(sensors)
186 }
187
188 pub fn save_to_file(&self, sensors: &[Sensor], path: &Path) -> Result<()> {
190 let sensor_configs: Vec<SensorConfig> = sensors
191 .iter()
192 .map(|s| SensorConfig::from(s.clone()))
193 .collect();
194
195 let config_file = SensorConfigFile {
196 version: "1.0".to_string(),
197 sensors: sensor_configs,
198 };
199
200 let content = toml::to_string_pretty(&config_file).map_err(GlintError::Serialization)?;
201
202 if let Some(parent) = path.parent() {
204 std::fs::create_dir_all(parent).map_err(GlintError::Io)?;
205 }
206
207 std::fs::write(path, content).map_err(GlintError::Io)?;
208
209 Ok(())
210 }
211
212 pub fn load_default_config(&self) -> Result<Vec<Sensor>> {
214 let config_dir = self.get_config_dir()?;
215 let config_path = config_dir.join("sensors.toml");
216
217 if config_path.exists() {
218 self.load_from_file(&config_path)
219 } else {
220 Ok(Vec::new())
222 }
223 }
224
225 pub fn save_default_config(&self, sensors: &[Sensor]) -> Result<()> {
227 let config_dir = self.get_config_dir()?;
228 let config_path = config_dir.join("sensors.toml");
229 self.save_to_file(sensors, &config_path)
230 }
231
232 pub fn create_sample_config(&self, path: &Path) -> Result<()> {
234 let registry = crate::core::sensor::SensorRegistry::with_defaults();
235 let default_sensors: Vec<_> = registry.sensors().into_iter().cloned().collect();
236 self.save_to_file(&default_sensors, path)
237 }
238
239 pub fn load_into_registry(&self, registry: &mut SensorRegistry) -> Result<()> {
241 match self.load_default_config() {
243 Ok(sensors) => {
244 for sensor in sensors {
245 registry.register_sensor(sensor)?;
246 }
247 }
248 Err(GlintError::Io(_)) => {
249 }
251 Err(e) => return Err(e),
252 }
253
254 Ok(())
255 }
256}
257
258impl Default for ConfigManager {
259 fn default() -> Self {
260 Self::new()
261 }
262}
263
264#[cfg(test)]
265mod tests {
266 use super::*;
267 use crate::core::sensor::bands;
268 use tempfile::tempdir;
269
270 #[test]
271 fn test_sensor_config_conversion() {
272 let sensor = Sensor::new(
273 "test",
274 "Test Sensor",
275 vec![bands::red(), bands::green(), bands::blue()],
276 8,
277 "single_file",
278 )
279 .with_description("Test description");
280
281 let config = SensorConfig::from(sensor.clone());
283 let converted_sensor = Sensor::from(config);
284
285 assert_eq!(sensor.id, converted_sensor.id);
286 assert_eq!(sensor.name, converted_sensor.name);
287 assert_eq!(sensor.description, converted_sensor.description);
288 assert_eq!(sensor.bit_depth, converted_sensor.bit_depth);
289 assert_eq!(sensor.loader_type, converted_sensor.loader_type);
290 assert_eq!(sensor.bands.len(), converted_sensor.bands.len());
291 }
292
293 #[test]
294 fn test_config_file_save_load() {
295 let temp_dir = tempdir().unwrap();
296 let config_path = temp_dir.path().join("sensors.toml");
297
298 let registry = crate::core::sensor::SensorRegistry::with_defaults();
299 let sensors = vec![
300 registry.get_sensor("rgb").unwrap().clone(),
301 registry.get_sensor("p4ms").unwrap().clone(),
302 ];
303
304 let manager = ConfigManager::new();
305
306 manager.save_to_file(&sensors, &config_path).unwrap();
308 assert!(config_path.exists());
309
310 let loaded_sensors = manager.load_from_file(&config_path).unwrap();
312 assert_eq!(loaded_sensors.len(), 2);
313
314 assert_eq!(loaded_sensors[0].id, "rgb");
316 assert_eq!(loaded_sensors[1].id, "p4ms");
317 }
318
319 #[test]
320 fn test_config_manager_with_directory() {
321 let temp_dir = tempdir().unwrap();
322 let manager = ConfigManager::with_config_dir(temp_dir.path().to_path_buf());
323
324 let registry = crate::core::sensor::SensorRegistry::with_defaults();
325 let sensors = vec![registry.get_sensor("rgb").unwrap().clone()];
326
327 manager.save_default_config(&sensors).unwrap();
329 let loaded_sensors = manager.load_default_config().unwrap();
330
331 assert_eq!(loaded_sensors.len(), 1);
332 assert_eq!(loaded_sensors[0].id, "rgb");
333 }
334
335 #[test]
336 fn test_sample_config_creation() {
337 let temp_dir = tempdir().unwrap();
338 let sample_path = temp_dir.path().join("sample_sensors.toml");
339
340 let manager = ConfigManager::new();
341 manager.create_sample_config(&sample_path).unwrap();
342
343 assert!(sample_path.exists());
344
345 let sensors = manager.load_from_file(&sample_path).unwrap();
347 assert_eq!(sensors.len(), 6); }
349
350 #[test]
351 fn test_registry_integration() {
352 let temp_dir = tempdir().unwrap();
353 let manager = ConfigManager::with_config_dir(temp_dir.path().to_path_buf());
354
355 let registry = crate::core::sensor::SensorRegistry::with_defaults();
357 let sensors = vec![
358 registry.get_sensor("rgb").unwrap().clone(),
359 registry.get_sensor("m3m").unwrap().clone(),
360 ];
361 manager.save_default_config(&sensors).unwrap();
362
363 let mut registry = SensorRegistry::new();
365 manager.load_into_registry(&mut registry).unwrap();
366
367 assert!(registry.has_sensor("rgb"));
368 assert!(registry.has_sensor("m3m"));
369 assert!(!registry.has_sensor("p4ms")); }
371}