177 lines
5.7 KiB
Rust
177 lines
5.7 KiB
Rust
//! Configuration persistence - loading and saving settings.
|
|
|
|
use std::fs;
|
|
use std::io::{self, Read, Write};
|
|
use std::path::PathBuf;
|
|
|
|
use tracing::{debug, info, warn};
|
|
|
|
use super::{get_config_dir, Settings, CONFIG_FILE_NAME};
|
|
use crate::error::{ConfigError, Result};
|
|
|
|
/// Configuration persistence handler.
|
|
pub struct ConfigPersistence {
|
|
config_path: PathBuf,
|
|
}
|
|
|
|
impl ConfigPersistence {
|
|
/// Create a new persistence handler with the given config path.
|
|
pub fn new(config_path: PathBuf) -> Self {
|
|
Self { config_path }
|
|
}
|
|
|
|
/// Create a persistence handler using the default config location.
|
|
pub fn default_location() -> io::Result<Self> {
|
|
let config_dir = get_config_dir()
|
|
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Config directory not found"))?;
|
|
|
|
Ok(Self::new(config_dir.join(CONFIG_FILE_NAME)))
|
|
}
|
|
|
|
/// Get the configuration file path.
|
|
pub fn config_path(&self) -> &PathBuf {
|
|
&self.config_path
|
|
}
|
|
|
|
/// Check if the configuration file exists.
|
|
pub fn exists(&self) -> bool {
|
|
self.config_path.exists()
|
|
}
|
|
|
|
/// Load settings from the configuration file.
|
|
pub fn load(&self) -> Result<Settings> {
|
|
if !self.exists() {
|
|
debug!("Config file not found, using defaults");
|
|
return Ok(Settings::default());
|
|
}
|
|
|
|
let mut file = fs::File::open(&self.config_path).map_err(ConfigError::ReadError)?;
|
|
|
|
let mut contents = String::new();
|
|
file.read_to_string(&mut contents)
|
|
.map_err(ConfigError::ReadError)?;
|
|
|
|
let settings: Settings = toml::from_str(&contents).map_err(ConfigError::from)?;
|
|
|
|
info!("Loaded configuration from {:?}", self.config_path);
|
|
Ok(settings)
|
|
}
|
|
|
|
/// Save settings to the configuration file.
|
|
pub fn save(&self, settings: &Settings) -> Result<()> {
|
|
// Ensure parent directory exists
|
|
if let Some(parent) = self.config_path.parent() {
|
|
if !parent.exists() {
|
|
fs::create_dir_all(parent).map_err(ConfigError::WriteError)?;
|
|
debug!("Created config directory: {:?}", parent);
|
|
}
|
|
}
|
|
|
|
let contents = toml::to_string_pretty(settings)
|
|
.map_err(|e| ConfigError::InvalidConfig(e.to_string()))?;
|
|
|
|
let mut file = fs::File::create(&self.config_path).map_err(ConfigError::WriteError)?;
|
|
|
|
file.write_all(contents.as_bytes())
|
|
.map_err(ConfigError::WriteError)?;
|
|
|
|
info!("Saved configuration to {:?}", self.config_path);
|
|
Ok(())
|
|
}
|
|
|
|
/// Validate settings and apply any necessary migrations.
|
|
pub fn validate_and_migrate(&self, settings: &mut Settings) -> Result<()> {
|
|
// Ensure output directory exists
|
|
if !settings.output.path.exists() {
|
|
fs::create_dir_all(&settings.output.path).map_err(|e| {
|
|
ConfigError::InvalidConfig(format!("Cannot create output directory: {}", e))
|
|
})?;
|
|
debug!("Created output directory: {:?}", settings.output.path);
|
|
}
|
|
|
|
// Validate frame rate
|
|
if settings.video.frame_rate == 0 || settings.video.frame_rate > 240 {
|
|
warn!(
|
|
"Invalid frame rate {}, defaulting to 60",
|
|
settings.video.frame_rate
|
|
);
|
|
settings.video.frame_rate = 60;
|
|
}
|
|
|
|
// Validate container format
|
|
let valid_containers = ["mp4", "mkv", "mov", "flv"];
|
|
if !valid_containers.contains(&settings.output.container.as_str()) {
|
|
warn!(
|
|
"Invalid container format '{}', defaulting to mp4",
|
|
settings.output.container
|
|
);
|
|
settings.output.container = "mp4".to_string();
|
|
}
|
|
|
|
// Validate log level
|
|
let valid_levels = ["trace", "debug", "info", "warn", "error"];
|
|
if !valid_levels.contains(&settings.daemon.log_level.as_str()) {
|
|
warn!(
|
|
"Invalid log level '{}', defaulting to info",
|
|
settings.daemon.log_level
|
|
);
|
|
settings.daemon.log_level = "info".to_string();
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Load configuration from the default location.
|
|
pub fn load_config() -> Result<Settings> {
|
|
let persistence = ConfigPersistence::default_location().map_err(|e| {
|
|
ConfigError::InvalidConfig(format!("Cannot access config directory: {}", e))
|
|
})?;
|
|
|
|
let mut settings = persistence.load()?;
|
|
persistence.validate_and_migrate(&mut settings)?;
|
|
|
|
Ok(settings)
|
|
}
|
|
|
|
/// Save configuration to the default location.
|
|
pub fn save_config(settings: &Settings) -> Result<()> {
|
|
let persistence = ConfigPersistence::default_location().map_err(|e| {
|
|
ConfigError::InvalidConfig(format!("Cannot access config directory: {}", e))
|
|
})?;
|
|
|
|
persistence.save(settings)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use tempfile::tempdir;
|
|
|
|
#[test]
|
|
fn test_save_and_load_config() {
|
|
let dir = tempdir().unwrap();
|
|
let config_path = dir.path().join("config.toml");
|
|
let persistence = ConfigPersistence::new(config_path);
|
|
|
|
let settings = Settings::default();
|
|
persistence.save(&settings).unwrap();
|
|
|
|
assert!(persistence.exists());
|
|
|
|
let loaded = persistence.load().unwrap();
|
|
assert_eq!(settings.video.frame_rate, loaded.video.frame_rate);
|
|
assert_eq!(settings.daemon.auto_record, loaded.daemon.auto_record);
|
|
}
|
|
|
|
#[test]
|
|
fn test_load_nonexistent_config() {
|
|
let dir = tempdir().unwrap();
|
|
let config_path = dir.path().join("nonexistent.toml");
|
|
let persistence = ConfigPersistence::new(config_path);
|
|
|
|
let settings = persistence.load().unwrap();
|
|
assert_eq!(settings.video.frame_rate, 60);
|
|
}
|
|
}
|