v0.2.5-pre.010.fix.001

This commit is contained in:
2026-08-20 11:17:12 +02:00
parent 5bf9651038
commit c0b131bf6f
97 changed files with 2277 additions and 655 deletions

View File

@@ -1,14 +1,14 @@
// file: crates/ksp-config-lib/src/bootstrap.rs
// version: 1
// version: 2
/// Default root containing KSP runtime configuration documents.
pub const DEFAULT_CFG_PATH: &str = "config";
/// Default root containing KSP JSON schemas.
pub const DEFAULT_SCHEMA_PATH: &str = "config/schemas";
/// Bootstrap argument used to replace the configuration document root.
pub const ARG_CFG_PATH: &str = "--cfgpath";
/// Bootstrap argument used to replace the schema root.
pub const ARG_SCHEMA_PATH: &str = "--schemapath";
/// Default root containing KSP runtime configuration documents.
pub const DEFAULT_CFG_PATH: &str = "config";
/// Default root containing KSP JSON schemas.
pub const DEFAULT_SCHEMA_PATH: &str = "config/schemas";
/// Non-recursive bootstrap options required before Config can resolve any managed document.
#[derive(Clone, Debug, Eq, PartialEq)]

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/src/composite.rs
// version: 1
// version: 2
/// One resolved document component selected by a composite profile.
#[derive(Clone, Debug, PartialEq)]
@@ -118,6 +118,7 @@ struct CompositeDocumentReferenceSource {
profile_id: std::option::Option<String>,
}
/// Validates composite document contract.
pub(crate) fn validate_composite_document_contract(engine: &crate::ConfigDocumentEngine, document: &crate::ConfigJsonDocument) -> ksp_core_lib::Result<()> {
if !document.file_id().as_str().starts_with("cfg.composite.") {
return std::result::Result::Ok(());
@@ -198,12 +199,8 @@ fn resolve_composite_document(
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let resolved = crate::profile::load_resolved_profile_with_source(
engine,
&file_id,
reference.profile_id.as_deref(),
crate::ConfigProfileSelectionSource::Composite,
);
let resolved =
crate::load_resolved_profile_with_source(engine, &file_id, reference.profile_id.as_deref(), crate::ConfigProfileSelectionSource::Composite);
let resolved = match resolved {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -262,8 +259,7 @@ fn validate_reference(
"referenced file_id is not a Config document",
));
}
let resolved =
crate::profile::load_resolved_profile_with_source(engine, &file_id, reference.profile_id.as_deref(), crate::ConfigProfileSelectionSource::Composite);
let resolved = crate::load_resolved_profile_with_source(engine, &file_id, reference.profile_id.as_deref(), crate::ConfigProfileSelectionSource::Composite);
if let std::result::Result::Err(error) = resolved {
return std::result::Result::Err(error);
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/src/document.rs
// version: 5
// version: 6
/// A Config-managed JSON document that has passed syntax, schema and current semantic validation.
#[derive(Clone, Debug, PartialEq)]
@@ -85,6 +85,7 @@ impl ConfigDocumentEngine {
return self.validate_document(document, &schema_file_id);
}
/// Validates source candidate.
pub(crate) fn validate_source_candidate(&self, file_id: &crate::ConfigFileId, source: &str) -> ksp_core_lib::Result<ConfigJsonDocument> {
let path = self.registry.resolve_path(&self.bootstrap, file_id);
let path = match path {
@@ -99,6 +100,7 @@ impl ConfigDocumentEngine {
return self.validate_candidate(file_id, value);
}
/// Validates candidate.
pub(crate) fn validate_candidate(&self, file_id: &crate::ConfigFileId, value: serde_json::Value) -> ksp_core_lib::Result<ConfigJsonDocument> {
let descriptor = self.registry.descriptor(file_id);
let descriptor = match descriptor {
@@ -256,11 +258,11 @@ fn validate_instance(document: &ConfigJsonDocument, schema: &ConfigJsonDocument)
}
fn validate_document_semantics(engine: &ConfigDocumentEngine, document: &ConfigJsonDocument) -> ksp_core_lib::Result<()> {
let profile_validation = crate::profile::validate_document_profile_contract(document);
let profile_validation = crate::validate_document_profile_contract(document);
if let std::result::Result::Err(error) = profile_validation {
return std::result::Result::Err(error);
}
let composite_validation = crate::composite::validate_composite_document_contract(engine, document);
let composite_validation = crate::validate_composite_document_contract(engine, document);
if let std::result::Result::Err(error) = composite_validation {
return std::result::Result::Err(error);
}

View File

@@ -1,12 +1,10 @@
// file: crates/ksp-config-lib/src/environment.rs
// version: 6
/// Default local environment file read by Config from the process launch directory.
pub const DEFAULT_DOTENV_PATH: &str = ".env";
// version: 7
/// Versioned environment contract template expected at the repository/runtime root.
pub const DEFAULT_DOTENV_EXAMPLE_PATH: &str = ".env.example";
/// Default local environment file read by Config from the process launch directory.
pub const DEFAULT_DOTENV_PATH: &str = ".env";
const LOGGING_DOMAIN: &str = "config.environment";
/// Source that supplied one resolved Config environment variable.
@@ -230,6 +228,7 @@ impl ConfigEnvironment {
};
}
/// Loads from dotenv path.
pub(crate) fn load_from_dotenv_path(dotenv_path: &std::path::Path) -> ksp_core_lib::Result<Self> {
let process = collect_process_environment(std::env::vars_os());
let process = match process {
@@ -244,20 +243,24 @@ impl ConfigEnvironment {
return std::result::Result::Ok(Self { process, dotenv, dotenv_path: dotenv_path.to_path_buf() });
}
/// Returns the current process values.
pub(crate) const fn process_values(&self) -> &std::collections::BTreeMap<String, String> {
return &self.process;
}
/// Returns the current dotenv values.
pub(crate) const fn dotenv_values(&self) -> &std::collections::BTreeMap<String, String> {
return &self.dotenv;
}
/// Builds `ConfigEnvironment` from maps.
#[cfg(test)]
pub(crate) fn from_maps(process: std::collections::BTreeMap<String, String>, dotenv: std::collections::BTreeMap<String, String>) -> Self {
return Self { process, dotenv, dotenv_path: std::path::PathBuf::from(DEFAULT_DOTENV_PATH) };
}
}
/// Executes the crate-internal parse dotenv content operation for the owning module.
pub(crate) fn parse_dotenv_content(path: &std::path::Path, content: &str) -> ksp_core_lib::Result<std::collections::BTreeMap<String, String>> {
let mut output = std::collections::BTreeMap::<String, String>::new();
for (line_index, raw_line) in content.lines().enumerate() {
@@ -305,6 +308,7 @@ pub(crate) fn parse_dotenv_content(path: &std::path::Path, content: &str) -> ksp
return std::result::Result::Ok(output);
}
/// Validates supported variable name.
pub(crate) fn validate_supported_variable_name(variable_name: &str) -> ksp_core_lib::Result<()> {
if !has_supported_namespace(variable_name) {
return std::result::Result::Err(invalid_variable_error(variable_name, "variable must use the KSP_ or KSPB_ namespace"));

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/src/lib.rs
// version: 13
// version: 14
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -171,6 +171,21 @@ pub use self::sensitivity::ResolvedConfigText;
/// Effective standard HTTP Transport configuration mapped to `ksp_onchain_transport_lib::HttpTransportSettings`.
pub use self::transport::ResolvedTransportConfig;
/// Validates composite document contract.
pub(crate) use self::composite::validate_composite_document_contract;
/// Owning tracing target for events emitted by the Config crate.
pub(crate) use self::constants::TRACING_TARGET;
/// Executes the crate-internal parse dotenv content operation for the owning module.
pub(crate) use self::environment::parse_dotenv_content;
/// Validates supported variable name.
pub(crate) use self::environment::validate_supported_variable_name;
/// Executes the crate-internal atomic write operation for the owning module.
pub(crate) use self::persistence::atomic_write;
/// Executes the crate-internal atomic write private operation for the owning module.
pub(crate) use self::persistence::atomic_write_private;
/// Loads resolved profile with source.
pub(crate) use self::profile::load_resolved_profile_with_source;
/// Validates document profile contract.
pub(crate) use self::profile::validate_document_profile_contract;
/// Executes the crate-internal build registry operation for the owning module.
pub(crate) use self::registry::build_registry;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/src/management.rs
// version: 5
// version: 6
/// Raw source of one registered Config document read for explicit management/correction.
#[derive(Clone, Eq, PartialEq)]
@@ -727,7 +727,7 @@ impl ConfigManagement {
/// Authentication/authorization of the human user belongs to the calling application. Calling this method is the explicit Config boundary that opts into
/// real-value access; the returned value must never be logged.
pub fn reveal_effective_environment_value(&self, variable_name: &str) -> ksp_core_lib::Result<std::option::Option<String>> {
let validation = crate::environment::validate_supported_variable_name(variable_name);
let validation = crate::validate_supported_variable_name(variable_name);
if let std::result::Result::Err(error) = validation {
return std::result::Result::Err(error);
}
@@ -744,7 +744,7 @@ impl ConfigManagement {
/// Explicitly reveals the real persisted `.env` value for management display/editing.
pub fn reveal_dotenv_value(&self, variable_name: &str) -> ksp_core_lib::Result<std::option::Option<String>> {
let validation = crate::environment::validate_supported_variable_name(variable_name);
let validation = crate::validate_supported_variable_name(variable_name);
if let std::result::Result::Err(error) = validation {
return std::result::Result::Err(error);
}
@@ -758,7 +758,7 @@ impl ConfigManagement {
/// Creates or updates one supported KSP/KSPB `.env` entry atomically without mutating the inherited process environment.
pub fn set_dotenv_value(&self, variable_name: &str, value: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeReport> {
let validation = crate::environment::validate_supported_variable_name(variable_name);
let validation = crate::validate_supported_variable_name(variable_name);
if let std::result::Result::Err(error) = validation {
return std::result::Result::Err(error);
}
@@ -781,7 +781,7 @@ impl ConfigManagement {
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if candidate != content {
let write = crate::persistence::atomic_write_private(self.dotenv_path.as_path(), candidate.as_bytes());
let write = crate::atomic_write_private(self.dotenv_path.as_path(), candidate.as_bytes());
match write {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -797,7 +797,7 @@ impl ConfigManagement {
/// Removes one supported KSP/KSPB `.env` entry atomically without mutating the inherited process environment.
pub fn remove_dotenv_value(&self, variable_name: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeReport> {
let validation = crate::environment::validate_supported_variable_name(variable_name);
let validation = crate::validate_supported_variable_name(variable_name);
if let std::result::Result::Err(error) = validation {
return std::result::Result::Err(error);
}
@@ -820,7 +820,7 @@ impl ConfigManagement {
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if candidate != content {
let write = crate::persistence::atomic_write_private(self.dotenv_path.as_path(), candidate.as_bytes());
let write = crate::atomic_write_private(self.dotenv_path.as_path(), candidate.as_bytes());
match write {
std::result::Result::Ok(()) => {},
std::result::Result::Err(error) => return std::result::Result::Err(error),
@@ -838,6 +838,7 @@ impl ConfigManagement {
return crate::ConfigEnvironment::load_from_dotenv_path(self.dotenv_path.as_path());
}
/// Executes the crate-internal with dotenv path operation for `ConfigManagement`.
#[cfg(test)]
pub(crate) fn with_dotenv_path(engine: crate::ConfigDocumentEngine, dotenv_path: std::path::PathBuf) -> Self {
return Self { engine, dotenv_path };
@@ -1030,7 +1031,7 @@ fn persist_document_source(file_id: &crate::ConfigFileId, path: &std::path::Path
if existing.as_slice() == source {
return std::result::Result::Ok(ConfigDocumentChangeReport { source_changed: false, reload_required: false });
}
let write = crate::persistence::atomic_write(path, source);
let write = crate::atomic_write(path, source);
if let std::result::Result::Err(error) = write {
return std::result::Result::Err(error);
}

View File

@@ -1,12 +1,14 @@
// file: crates/ksp-config-lib/src/persistence.rs
// version: 3
// version: 4
static NEXT_TEMPORARY_FILE_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
/// Executes the crate-internal atomic write operation for the owning module.
pub(crate) fn atomic_write(path: &std::path::Path, content: &[u8]) -> ksp_core_lib::Result<()> {
return atomic_write_with_policy(path, content, false);
}
/// Executes the crate-internal atomic write private operation for the owning module.
pub(crate) fn atomic_write_private(path: &std::path::Path, content: &[u8]) -> ksp_core_lib::Result<()> {
return atomic_write_with_policy(path, content, true);
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/src/profile.rs
// version: 4
// version: 5
/// Origin of one top-level value in a resolved standard Config profile.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
@@ -135,6 +135,7 @@ impl crate::ConfigDocumentEngine {
}
}
/// Loads resolved profile with source.
pub(crate) fn load_resolved_profile_with_source(
engine: &crate::ConfigDocumentEngine,
file_id: &crate::ConfigFileId,
@@ -153,6 +154,7 @@ pub(crate) fn load_resolved_profile_with_source(
return resolve_document_profile(&document, requested_profile, source);
}
/// Validates document profile contract.
pub(crate) fn validate_document_profile_contract(document: &crate::ConfigJsonDocument) -> ksp_core_lib::Result<()> {
let root = match document.value().as_object() {
std::option::Option::Some(value) => value,

View File

@@ -1,18 +1,10 @@
// file: crates/ksp-config-lib/src/registry.rs
// version: 6
// version: 7
/// Bootstrap argument used to replace a known Config filename mapping.
pub const ARG_FILE_MAP: &str = "--filemap";
/// Logical file identifier for the standard Logging configuration document.
pub const FILE_ID_STD_LOGGING: &str = "cfg.std.logging";
/// Logical file identifier for the standard Logging JSON Schema document.
pub const FILE_ID_SCHEMA_STD_LOGGING: &str = "schema.std.logging";
/// Logical file identifier for the standard HTTP Transport configuration document.
pub const FILE_ID_STD_TRANSPORT: &str = "cfg.std.transport";
/// Logical file identifier for the standard HTTP Transport JSON Schema document.
pub const FILE_ID_SCHEMA_STD_TRANSPORT: &str = "schema.std.transport";
/// Logical file identifier for the generic composite JSON Schema document.
pub const FILE_ID_SCHEMA_COMPOSITE: &str = "schema.composite";
/// Default physical filename for the generic composite JSON Schema document.
pub const DEFAULT_COMPOSITE_SCHEMA_FILENAME: &str = "composite.schema.json";
/// Default physical filename for the standard Logging configuration document.
pub const DEFAULT_STD_LOGGING_FILENAME: &str = "std.logging.json";
/// Default physical filename for the standard Logging JSON Schema document.
@@ -21,8 +13,16 @@ pub const DEFAULT_STD_LOGGING_SCHEMA_FILENAME: &str = "std.logging.schema.json";
pub const DEFAULT_STD_TRANSPORT_FILENAME: &str = "std.transport.json";
/// Default physical filename for the standard HTTP Transport JSON Schema document.
pub const DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME: &str = "std.transport.schema.json";
/// Default physical filename for the generic composite JSON Schema document.
pub const DEFAULT_COMPOSITE_SCHEMA_FILENAME: &str = "composite.schema.json";
/// Logical file identifier for the generic composite JSON Schema document.
pub const FILE_ID_SCHEMA_COMPOSITE: &str = "schema.composite";
/// Logical file identifier for the standard Logging JSON Schema document.
pub const FILE_ID_SCHEMA_STD_LOGGING: &str = "schema.std.logging";
/// Logical file identifier for the standard HTTP Transport JSON Schema document.
pub const FILE_ID_SCHEMA_STD_TRANSPORT: &str = "schema.std.transport";
/// Logical file identifier for the standard Logging configuration document.
pub const FILE_ID_STD_LOGGING: &str = "cfg.std.logging";
/// Logical file identifier for the standard HTTP Transport configuration document.
pub const FILE_ID_STD_TRANSPORT: &str = "cfg.std.transport";
/// Stable logical identifier for a Config-managed file.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
@@ -89,6 +89,7 @@ impl ConfigFileDescriptor {
return self.schema_file_id.as_ref();
}
/// Creates a new `ConfigFileDescriptor` value.
pub(crate) fn new(
file_id: &'static str,
kind: ConfigFileKind,
@@ -249,6 +250,7 @@ impl ConfigFileRegistry {
}
}
/// Executes the crate-internal build registry operation for the owning module.
pub(crate) fn build_registry<const N: usize>(descriptors: [ConfigFileDescriptor; N]) -> ksp_core_lib::Result<ConfigFileRegistry> {
let mut registry = ConfigFileRegistry { descriptors: std::collections::BTreeMap::new() };
for descriptor in descriptors {

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-config-lib/src/sensitivity.rs
// version: 1
// version: 2
/// Replacement used for secret environment fragments in safe diagnostic representations.
pub const REDACTED_CONFIG_VALUE: &str = "********";
@@ -18,7 +18,7 @@ pub enum ConfigSensitivity {
impl ConfigSensitivity {
/// Classifies one supported KSP/KSPB environment variable by its namespace.
pub fn from_variable_name(variable_name: &str) -> ksp_core_lib::Result<Self> {
let validation = crate::environment::validate_supported_variable_name(variable_name);
let validation = crate::validate_supported_variable_name(variable_name);
if let std::result::Result::Err(error) = validation {
return std::result::Result::Err(error);
}
@@ -103,6 +103,7 @@ pub struct ResolvedConfigText {
}
impl ResolvedConfigText {
/// Creates a new `ResolvedConfigText` value.
pub(crate) fn new(value: String, safe_value: String, sensitivity: ConfigSensitivity, provenance: std::vec::Vec<ConfigValueProvenance>) -> Self {
return Self { value, safe_value, sensitivity, provenance };
}
@@ -153,6 +154,7 @@ pub struct ResolvedConfigJson {
}
impl ResolvedConfigJson {
/// Creates a new `ResolvedConfigJson` value.
pub(crate) fn new(
value: serde_json::Value,
safe_value: serde_json::Value,

View File

@@ -1,8 +1,8 @@
// file: crates/ksp-config-lib/unit_tests/composite.rs
// version: 3
// version: 4
const TEST_COMPOSITE_FILE_ID: &str = "cfg.composite.test";
const TEST_COMPOSITE_FILENAME: &str = "examples/composite.example.json";
const TEST_COMPOSITE_FILE_ID: &str = "cfg.composite.test";
#[test]
fn fixture_composite_example_resolves_default_document_profile() {
@@ -197,7 +197,7 @@ fn test_registry(composite_filename: &'static str) -> ksp_core_lib::Result<crate
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return crate::registry::build_registry([logging, logging_schema, composite_schema, composite]);
return crate::build_registry([logging, logging_schema, composite_schema, composite]);
}
fn prepare_fixture(fixture: &FixtureRoots, composite: &str) -> std::io::Result<()> {