// file: crates/ksp-config-lib/src/store.rs // version: 3 /// Effective standard Store configuration mapped to `ksp_store_lib::StoreSettings`. pub struct ResolvedStoreConfig { effective: crate::ResolvedConfigJson, file_id: crate::ConfigFileId, profile_id: String, selection_source: crate::ConfigProfileSelectionSource, settings: ksp_store_lib::StoreSettings, source_path: std::path::PathBuf, } impl ResolvedStoreConfig { /// Returns the detailed environment-resolved Config view. #[must_use] pub const fn effective(&self) -> &crate::ResolvedConfigJson { return &self.effective; } /// Returns the logical Config file identifier used by this runtime configuration. #[must_use] pub const fn file_id(&self) -> &crate::ConfigFileId { return &self.file_id; } /// Returns the selected standard Store profile identifier. /// /// For `std.store`, the profile identifier is also the stable named Store target identifier. #[must_use] pub fn profile_id(&self) -> &str { return self.profile_id.as_str(); } /// Returns the selected named Store target identifier. #[must_use] pub fn target_id(&self) -> &str { return self.profile_id.as_str(); } /// Returns the source that selected the standard Store profile. #[must_use] pub const fn selection_source(&self) -> crate::ConfigProfileSelectionSource { return self.selection_source; } /// Borrows the backend-neutral Store settings without exposing the connection URI. #[must_use] pub const fn settings(&self) -> &ksp_store_lib::StoreSettings { return &self.settings; } /// Consumes the resolved Config and returns the Store-owned runtime settings. #[must_use] pub fn into_settings(self) -> ksp_store_lib::StoreSettings { return self.settings; } /// Returns the physical source Config document path. #[must_use] pub fn source_path(&self) -> &std::path::Path { return self.source_path.as_path(); } } impl std::fmt::Debug for ResolvedStoreConfig { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { return formatter .debug_struct("ResolvedStoreConfig") .field("effective", &self.effective) .field("file_id", &self.file_id) .field("profile_id", &self.profile_id) .field("selection_source", &self.selection_source) .field("settings", &self.settings) .field("source_path", &self.source_path) .finish(); } } impl crate::ConfigDocumentEngine { /// Loads `std.store`, resolves one profile/environment and maps it to backend-neutral Store settings. pub fn load_resolved_store_config( &self, requested_profile: std::option::Option<&str>, environment: &crate::ConfigEnvironment, ) -> ksp_core_lib::Result { let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_STORE); let file_id = match file_id { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let profile = self.load_resolved_profile(&file_id, requested_profile); let profile = match profile { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; return resolve_store_profile(&profile, environment); } /// Maps an already resolved `cfg.std.store` profile while preserving its selection provenance. pub fn resolve_store_config_profile( &self, profile: &crate::ResolvedConfigProfile, environment: &crate::ConfigEnvironment, ) -> ksp_core_lib::Result { if profile.file_id().as_str() != crate::FILE_ID_STD_STORE { return std::result::Result::Err(effective_error(profile, "resolved Config profile does not reference the standard Store document")); } let descriptor = self.registry().descriptor(profile.file_id()); if let std::result::Result::Err(error) = descriptor { return std::result::Result::Err(error); } return resolve_store_profile(profile, environment); } } #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct EffectiveStoreSource { backend: String, format_version: u32, network: String, postgres: EffectivePostgresSource, profile_id: String, } #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct EffectivePostgresSource { bootstrap: EffectivePostgresBootstrapSource, connection_uri: String, pool: EffectivePostgresPoolSource, shutdown_timeout_ms: u64, tls: EffectivePostgresTlsSource, } #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct EffectivePostgresPoolSource { connect_timeout_ms: u64, create_timeout_ms: u64, max_connections: u32, recycle_timeout_ms: u64, wait_timeout_ms: u64, } #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct EffectivePostgresTlsSource { mode: String, } #[derive(serde::Deserialize)] #[serde(deny_unknown_fields)] struct EffectivePostgresBootstrapSource { auto_migrate: std::option::Option, migration_lock_timeout_ms: u64, migration_timeout_ms: u64, schema_autocreate: std::option::Option, schema_autoupdate: std::option::Option, } fn resolve_store_profile(profile: &crate::ResolvedConfigProfile, environment: &crate::ConfigEnvironment) -> ksp_core_lib::Result { ksp_logging_lib::trace!(target: crate::TRACING_TARGET, profile_id = profile.profile_id(), "mapping standard Store Config profile"); let effective = profile.resolve_effective_environment_detailed(environment); let effective = match effective { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; let provenance = validate_connection_uri_provenance(&effective, profile); if let std::result::Result::Err(error) = provenance { return std::result::Result::Err(error); } let source = serde_json::from_value::(effective.value().clone()); let source = match source { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => { return std::result::Result::Err( effective_error(profile, "effective Store Config cannot be decoded into the runtime adapter contract").with_source(error), ); }, }; if source.format_version != 1 && source.format_version != 2 { return std::result::Result::Err(effective_error(profile, "effective Store format_version is unsupported")); } if source.profile_id != profile.profile_id() { return std::result::Result::Err(effective_error(profile, "effective Store profile_id does not match the selected profile")); } if source.backend != "postgres" { return std::result::Result::Err(effective_error(profile, "effective Store backend is unsupported").with_context("backend", source.backend)); } let tls_mode = match source.postgres.tls.mode.as_str() { "disabled" => ksp_store_lib::PostgresTlsMode::Disabled, "verify_full" => ksp_store_lib::PostgresTlsMode::VerifyFull, _ => return std::result::Result::Err(effective_error(profile, "effective Store PostgreSQL TLS mode is unsupported")), }; let pool = ksp_store_lib::PostgresPoolSettings::new( source.postgres.pool.max_connections, std::time::Duration::from_millis(source.postgres.pool.connect_timeout_ms), std::time::Duration::from_millis(source.postgres.pool.wait_timeout_ms), std::time::Duration::from_millis(source.postgres.pool.create_timeout_ms), std::time::Duration::from_millis(source.postgres.pool.recycle_timeout_ms), ); let bootstrap = match source.format_version { 1 => { let auto_migrate = match (source.postgres.bootstrap.auto_migrate, source.postgres.bootstrap.schema_autocreate, source.postgres.bootstrap.schema_autoupdate) { (std::option::Option::Some(value), std::option::Option::None, std::option::Option::None) => value, _ => return std::result::Result::Err(effective_error(profile, "effective Store V1 bootstrap policy is invalid")), }; ksp_store_lib::PostgresBootstrapSettings::new( auto_migrate, std::time::Duration::from_millis(source.postgres.bootstrap.migration_timeout_ms), std::time::Duration::from_millis(source.postgres.bootstrap.migration_lock_timeout_ms), ) }, 2 => { let (schema_autocreate, schema_autoupdate) = match (source.postgres.bootstrap.auto_migrate, source.postgres.bootstrap.schema_autocreate, source.postgres.bootstrap.schema_autoupdate) { (std::option::Option::None, std::option::Option::Some(autocreate), std::option::Option::Some(autoupdate)) => (autocreate, autoupdate), _ => return std::result::Result::Err(effective_error(profile, "effective Store V2 bootstrap policy is invalid")), }; ksp_store_lib::PostgresBootstrapSettings::with_schema_policy( schema_autocreate, schema_autoupdate, std::time::Duration::from_millis(source.postgres.bootstrap.migration_timeout_ms), std::time::Duration::from_millis(source.postgres.bootstrap.migration_lock_timeout_ms), ) }, _ => return std::result::Result::Err(effective_error(profile, "effective Store format_version is unsupported")), }; let network = ksp_store_lib::RawNetworkId::new(source.network); let network = match network { std::result::Result::Ok(value) => value, std::result::Result::Err(_) => return std::result::Result::Err(effective_error(profile, "effective Store network identifier is invalid")), }; let postgres = ksp_store_lib::PostgresStoreSettings::new(source.postgres.connection_uri, pool, tls_mode, bootstrap); let settings = ksp_store_lib::StoreSettings::new( network, ksp_store_lib::StoreBackendSettings::Postgres(postgres), std::time::Duration::from_millis(source.postgres.shutdown_timeout_ms), ); if let std::result::Result::Err(error) = settings.validate() { return std::result::Result::Err(store_contract_error(profile, &error)); } ksp_logging_lib::debug!( target: crate::TRACING_TARGET, profile_id = profile.profile_id(), network = settings.network().as_str(), backend = settings.backend_kind().code(), "mapped standard Store Config to Store settings" ); return std::result::Result::Ok(ResolvedStoreConfig { effective, file_id: profile.file_id().clone(), profile_id: profile.profile_id().to_owned(), selection_source: profile.selection_source(), settings, source_path: profile.path().to_path_buf(), }); } fn validate_connection_uri_provenance(effective: &crate::ResolvedConfigJson, profile: &crate::ResolvedConfigProfile) -> ksp_core_lib::Result<()> { let provenance = match effective.provenance_at("/postgres/connection_uri") { std::option::Option::Some(value) => value, std::option::Option::None => return std::result::Result::Err(effective_error(profile, "Store PostgreSQL connection URI provenance is unavailable")), }; let mut has_secret_environment = false; for item in provenance { let variable_name = match item.variable_name() { std::option::Option::Some(value) => value, std::option::Option::None => continue, }; let sensitivity = crate::ConfigSensitivity::from_variable_name(variable_name); let sensitivity = match sensitivity { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; if !sensitivity.is_secret() { return std::result::Result::Err(effective_error(profile, "Store PostgreSQL connection URI may reference only secret environment variables")); } has_secret_environment = true; } if !has_secret_environment { return std::result::Result::Err(effective_error(profile, "Store PostgreSQL connection URI requires secret environment provenance")); } return std::result::Result::Ok(()); } fn store_contract_error(profile: &crate::ResolvedConfigProfile, error: &ksp_core_lib::Error) -> ksp_core_lib::Error { return effective_error(profile, "effective Store settings fail the Store runtime contract") .with_context("store_error_domain", error.code().domain()) .with_context("store_error_code", error.code().code()); } fn effective_error(profile: &crate::ResolvedConfigProfile, reason: &'static str) -> ksp_core_lib::Error { return ksp_core_lib::Error::new(crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID, "effective Config cannot be mapped to the requested runtime contract") .with_context("file_id", profile.file_id().as_str()) .with_context("profile_id", profile.profile_id()) .with_context("reason", reason); } #[cfg(test)] #[path = "../unit_tests/store.rs"] mod tests;