#[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 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(super) config: SettingsConfig<S, Schema>,
pub(super) storage: S,
pub(super) settings_dir: RwLock<std::path::PathBuf>,
pub(super) sub_settings: RwLock<HashMap<String, Arc<SubSettings<S>>>>,
pub(super) events: Arc<EventManager>,
pub(super) settings_cache: SettingsCache,
pub(super) settings_write_lock: Mutex<()>,
pub(super) env_handler: EnvironmentHandler,
pub(super) schema_defaults: Arc<HashMap<String, Value>>,
pub(super) schema_metadata: Arc<HashMap<String, SettingMetadata>>,
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
pub(super) credentials: Option<CredentialManager>,
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
pub(super) is_upgraded: Arc<std::sync::atomic::AtomicBool>,
#[cfg(feature = "backup")]
pub(crate) external_providers: RwLock<Vec<Box<dyn ExternalConfigProvider>>>,
#[cfg(feature = "profiles")]
pub(super) profile_manager: Option<crate::profiles::ProfileManager<S>>,
pub(super) _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 => {
log::debug!("Credential management enabled with default backend");
Some(CredentialManager::new(&config.app_name))
}
#[cfg(all(feature = "keychain", feature = "encrypted-file"))]
CredentialConfig::WithFallback {
fallback_path,
password,
} => {
log::debug!(
"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) => {
log::debug!("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());
log::debug!(
"Initialized rcman SettingsManager at: {:?}",
config.config_dir.display()
);
let manager = 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(any(feature = "keychain", feature = "encrypted-file"))]
is_upgraded: Arc::new(std::sync::atomic::AtomicBool::new(false)),
#[cfg(feature = "backup")]
external_providers: RwLock::new(Vec::new()),
#[cfg(feature = "profiles")]
profile_manager,
_schema: PhantomData,
};
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
manager.migrate_secret_keys()?;
Ok(manager)
}
pub fn config(&self) -> &SettingsConfig<S, Schema> {
&self.config
}
pub fn storage(&self) -> &S {
&self.storage
}
pub fn events(&self) -> &EventManager {
&self.events
}
#[cfg(any(feature = "keychain", feature = "encrypted-file"))]
pub fn credentials(&self) -> Option<&crate::credentials::CredentialManager> {
self.credentials.as_ref()
}
#[cfg(all(
feature = "backup",
any(feature = "keychain", feature = "encrypted-file")
))]
pub(crate) fn schema_metadata(&self) -> &HashMap<String, SettingMetadata> {
&self.schema_metadata
}
}
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,
)
}
}