//! Settings and app-config use-case implementation. //! //! `SettingsServiceImpl` implements [`SettingsService`] from the domain //! layer. It is generic over `S: SettingsRepository` and `C: AppConfigRepository`, //! delegating persistence to those adapters. //! //! # Flow //! //! Each method delegates to the appropriate injected repository with the //! configured `base_dir`. The `update_provider` method coordinates between //! both repositories: load app config → mutate provider map → save app config. use std::path::PathBuf; use tracing; use zesdex_domain::cms::{ AppConfig, AppConfigRepository, ProviderConfig, ServiceError, Settings, SettingsRepository, }; /// Service implementation for settings and app-config operations. /// /// Generic over `S: SettingsRepository` and `C: AppConfigRepository` so /// the persistence layer can be swapped without changing business logic. pub struct SettingsServiceImpl { pub settings_repo: S, pub app_config_repo: C, pub base_dir: PathBuf, } impl SettingsServiceImpl { /// Create a new service with the given repositories and base directory. pub fn new( settings_repo: S, app_config_repo: C, base_dir: impl Into, ) -> Self { tracing::debug!("creating SettingsServiceImpl"); Self { settings_repo, app_config_repo, base_dir: base_dir.into(), } } } impl zesdex_domain::cms::SettingsService for SettingsServiceImpl { fn load_settings(&self) -> Result { tracing::debug!("loading settings"); self.settings_repo .load(&self.base_dir) .map_err(ServiceError::Repository) } fn save_settings(&self, settings: &Settings) -> Result<(), ServiceError> { tracing::debug!("saving settings"); self.settings_repo.save(&self.base_dir, settings)?; Ok(()) } fn update_provider( &self, name: &str, config: &ProviderConfig, ) -> Result<(), ServiceError> { tracing::debug!("updating provider '{name}'"); let mut app_config: AppConfig = self.app_config_repo.load(&self.base_dir)?; app_config .providers .insert(name.to_string(), config.clone()); self.app_config_repo.save(&self.base_dir, &app_config)?; Ok(()) } }