#[cfg(feature = "backup")]
use crate::backup::ExternalConfigProvider;
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
use crate::config::CredentialConfig;
use crate::config::{SettingMetadata, SettingsConfig, SettingsSchema};
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
use crate::credentials::CredentialManager;
use crate::error::Result;
use crate::manager::EventManager;
use crate::manager::cache::SettingsCache;
use crate::manager::env::EnvironmentHandler;
use crate::storage::StorageBackend;
use crate::sub_settings::SubSettings;
use log::info;
use serde_json::Value;
use std::collections::HashMap;
use std::marker::PhantomData;
use std::sync::{Arc, Mutex, RwLock};
pub struct SettingsManager<
S: StorageBackend = crate::storage::JsonStorage,
Schema: SettingsSchema = (),
> {
pub(crate) config: SettingsConfig<S, Schema>,
pub(crate) storage: S,
pub(crate) settings_dir: RwLock<std::path::PathBuf>,
pub(crate) sub_settings: RwLock<HashMap<String, Arc<SubSettings<S>>>>,
pub(crate) events: Arc<EventManager>,
pub(crate) settings_cache: SettingsCache,
pub(crate) settings_write_lock: Mutex<()>,
pub(crate) env_handler: EnvironmentHandler,
pub(crate) schema_defaults: Arc<HashMap<String, Value>>,
pub(crate) schema_metadata: Arc<HashMap<String, SettingMetadata>>,
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
pub(crate) credentials: Option<CredentialManager>,
#[cfg(feature = "backup")]
pub(crate) external_providers: RwLock<Vec<Box<dyn ExternalConfigProvider>>>,
#[cfg(feature = "profiles")]
pub(crate) profile_manager: Option<crate::profiles::ProfileManager<S>>,
pub(crate) _schema: PhantomData<Schema>,
}
impl<S: StorageBackend + 'static, Schema: SettingsSchema> SettingsManager<S, Schema> {
pub fn new(config: SettingsConfig<S, Schema>) -> Result<Self> {
if !config.config_dir.exists() {
crate::utils::security::ensure_secure_dir(&config.config_dir)?;
}
let storage = config.storage.clone();
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
let credentials = match &config.credential_config {
CredentialConfig::Disabled => None,
CredentialConfig::Default => {
info!("Credential management enabled with default backend");
Some(CredentialManager::new(&config.app_name))
}
#[cfg(all(feature = "keychain", feature = "encrypted-file"))]
CredentialConfig::WithFallback {
fallback_path,
password,
} => {
info!("Credential management enabled with keychain and encrypted file fallback");
let path = fallback_path
.clone()
.unwrap_or_else(|| config.config_dir.join("secrets.enc"));
Some(CredentialManager::with_fallback(
&config.app_name,
path,
password,
))
}
CredentialConfig::Custom(backend) => {
info!("Credential management enabled with custom backend");
Some(CredentialManager::with_backend(
&config.app_name,
backend.clone(),
))
}
};
#[cfg(feature = "profiles")]
let (settings_dir, profile_manager) = crate::profiles::ProfileManager::initialize(
&config.config_dir,
"settings",
storage.clone(),
config.profiles_enabled,
&config.profile_migrator,
)?;
#[cfg(not(feature = "profiles"))]
let settings_dir = config.config_dir.clone();
let metadata = Arc::new(Schema::get_metadata());
let schema_defaults = Arc::new(
metadata
.iter()
.map(|(k, m)| (k.clone(), m.default.clone()))
.collect(),
);
let env_handler =
EnvironmentHandler::new(config.env_prefix.clone(), config.env_source.clone());
info!(
"Initialized rcman SettingsManager at: {:?}",
config.config_dir.display()
);
Ok(Self {
config,
storage,
settings_dir: RwLock::new(settings_dir),
sub_settings: RwLock::new(HashMap::new()),
events: Arc::new(EventManager::new()),
settings_cache: SettingsCache::new(),
settings_write_lock: Mutex::new(()),
env_handler,
schema_defaults,
schema_metadata: metadata,
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
credentials,
#[cfg(feature = "backup")]
external_providers: RwLock::new(Vec::new()),
#[cfg(feature = "profiles")]
profile_manager,
_schema: PhantomData,
})
}
pub fn config(&self) -> &SettingsConfig<S, Schema> {
&self.config
}
pub fn storage(&self) -> &S {
&self.storage
}
pub fn events(&self) -> &Arc<EventManager> {
&self.events
}
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
pub fn credentials(&self) -> Option<&crate::credentials::CredentialManager> {
self.credentials.as_ref()
}
}
impl SettingsManager {
pub fn builder(
app_name: impl Into<String>,
app_version: impl Into<String>,
) -> crate::manager::SettingsManagerBuilder {
crate::manager::SettingsManagerBuilder::<crate::storage::JsonStorage, ()>::new(
app_name,
app_version,
)
}
}