v0.5.3-pre.002
This commit is contained in:
@@ -1,8 +1,19 @@
|
||||
<!-- file: ks-config/CHANGELOG.md -->
|
||||
<!-- version: 24 -->
|
||||
<!-- version: 26 -->
|
||||
|
||||
# CHANGELOG — ks-config
|
||||
|
||||
## `0.5.3-pre.002-delta-fix-001`
|
||||
|
||||
- aligne tous les contrats/types publics de `ks-config` sur la règle workspace imposant `crate::Item` depuis leur crate propriétaire, y compris dans leur module de déclaration et dans les tests ;
|
||||
- aligne les appels aux fonctions publiques réexportées sur le crate-root et ajoute le canari `KH_CFG015` pour empêcher le retour d'une référence locale non qualifiée à un type public de configuration.
|
||||
|
||||
## `0.5.3-pre.002`
|
||||
|
||||
- remplace les contrats runtime `PostgresConfig`/`SqliteConfig` par `DatabaseConfig { enabled, backend, backend_options }` afin que `ks-config` ne connaisse plus les paramètres d'un moteur non sélectionné ;
|
||||
- limite la validation store à la structure backend-agnostique et laisse `ks-store` interpréter/valider les options concrètes, sans modifier les conventions `KS_SECRET_*` ni la résolution des placeholders ;
|
||||
- aligne `store.config.json`, son exemple, le schéma spécialisé, le schéma runtime et les fixtures de configuration sur ce contrat opaque.
|
||||
|
||||
## `0.5.2-pre.006-delta-fix-007`
|
||||
|
||||
- corrige le test de séparation des répertoires wallet afin qu’il résolve d’abord `${KS_WALLETS_DIRECTORY:-wallets}` comme le chemin de chargement runtime ;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: ks-config/README.md -->
|
||||
<!-- version: 13 -->
|
||||
<!-- version: 14 -->
|
||||
|
||||
# ks-config
|
||||
|
||||
@@ -59,6 +59,8 @@ Les valeurs globales telles que `logs_directory` et `wallets_directory` ne sont
|
||||
|
||||
Depuis `0.5.1`, `ks-config` ne dépend plus de TS-RS et ne produit plus de bindings TypeScript. Ses contrats source/runtime peuvent contenir des secrets résolus : ils ne dérivent donc ni `serde::Serialize` ni `Debug`. Les applications propriétaires construisent leurs DTO publics et diagnostics bornés champ par champ.
|
||||
|
||||
Depuis `0.5.3-pre.002`, `DatabaseConfig` ne modélise plus PostgreSQL/SQLite : il conserve uniquement `enabled`, le code `backend` et un objet `backend_options` opaque. `ks-config` charge, compose et résout les placeholders ; la validation des paramètres du moteur et la création de la connexion appartiennent exclusivement à `ks-store`.
|
||||
|
||||
La section `application` d'une composition est volontairement opaque pour `ks-config`. Chaque binaire propriétaire définit son schéma et le valide avec `validate_json_value_against_schema`; le desktop utilise `config/schemas/kb-app-demo-desktop.application.config.schema.json`.
|
||||
|
||||
## Documents
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: ks-config/USAGE.md -->
|
||||
<!-- version: 12 -->
|
||||
<!-- version: 13 -->
|
||||
|
||||
# Utilisation de ks-config
|
||||
|
||||
@@ -88,7 +88,7 @@ Les endpoints WebSocket source sélectionnent une classe `ws_endpoint_defaults`;
|
||||
|
||||
## Store
|
||||
|
||||
`store.config.json` possède les paramètres de backend et les profils PostgreSQL/SQLite. Les helpers principaux sont `read_store_json_file_with_environment`, `default_store_profile` et `store_profile`.
|
||||
`store.config.json` sélectionne un backend par profil et transporte ses paramètres sous `backend_options`. Ce bloc reste opaque pour `ks-config` : seuls le code backend non vide et la forme objet sont validés ici. Les helpers principaux sont `read_store_json_file_with_environment`, `default_store_profile` et `store_profile`; `ks-store` interprète ensuite les options du backend sélectionné et crée la connexion.
|
||||
|
||||
## Wallet
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/composition.rs
|
||||
// version: 3
|
||||
// version: 5
|
||||
|
||||
//! Binary composition configuration and resolution into the shared runtime profile contract.
|
||||
|
||||
@@ -18,9 +18,9 @@ pub struct CompositionConfigDocument {
|
||||
/// Active binary composition profile name.
|
||||
pub active_profile: std::string::String,
|
||||
/// Shared configuration documents referenced by this composition.
|
||||
pub sources: CompositionConfigSources,
|
||||
pub sources: crate::CompositionConfigSources,
|
||||
/// Named binary composition profiles.
|
||||
pub profiles: std::vec::Vec<CompositionProfileConfig>,
|
||||
pub profiles: std::vec::Vec<crate::CompositionProfileConfig>,
|
||||
}
|
||||
|
||||
/// Paths of shared configuration documents consumed by one binary composition.
|
||||
@@ -65,7 +65,7 @@ pub struct CompositionProfileConfig {
|
||||
#[derive(Clone, Eq, PartialEq)]
|
||||
pub struct ComposedAppConfig {
|
||||
/// Parsed binary composition document.
|
||||
pub composition: CompositionConfigDocument,
|
||||
pub composition: crate::CompositionConfigDocument,
|
||||
/// Resolved runtime application configuration.
|
||||
pub app_config: crate::AppConfig,
|
||||
/// Resolved logging source path.
|
||||
@@ -101,7 +101,7 @@ pub fn composition_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||
|
||||
/// Validates raw binary composition JSON against its embedded schema.
|
||||
pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
let schema = match composition_json_schema_value() {
|
||||
let schema = match crate::composition_json_schema_value() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -133,12 +133,12 @@ pub fn validate_composition_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
}
|
||||
|
||||
/// Parses and validates one binary composition document.
|
||||
pub fn parse_composition_json(raw_json: &str) -> ks_core::Result<CompositionConfigDocument> {
|
||||
match validate_composition_json_schema(raw_json) {
|
||||
pub fn parse_composition_json(raw_json: &str) -> ks_core::Result<crate::CompositionConfigDocument> {
|
||||
match crate::validate_composition_json_schema(raw_json) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let document = match serde_json::from_str::<CompositionConfigDocument>(raw_json) {
|
||||
let document = match serde_json::from_str::<crate::CompositionConfigDocument>(raw_json) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
@@ -147,7 +147,7 @@ pub fn parse_composition_json(raw_json: &str) -> ks_core::Result<CompositionConf
|
||||
));
|
||||
},
|
||||
};
|
||||
return match validate_composition_document(&document) {
|
||||
return match crate::validate_composition_document(&document) {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(document),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -156,7 +156,7 @@ pub fn parse_composition_json(raw_json: &str) -> ks_core::Result<CompositionConf
|
||||
/// Reads and parses one binary composition document from a filesystem path.
|
||||
pub fn read_composition_json_file(
|
||||
path: &std::path::Path,
|
||||
) -> ks_core::Result<CompositionConfigDocument> {
|
||||
) -> ks_core::Result<crate::CompositionConfigDocument> {
|
||||
let raw_json = match std::fs::read_to_string(path) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
@@ -166,14 +166,14 @@ pub fn read_composition_json_file(
|
||||
));
|
||||
},
|
||||
};
|
||||
return parse_composition_json(&raw_json);
|
||||
return crate::parse_composition_json(&raw_json);
|
||||
}
|
||||
|
||||
/// Loads workspace environment values, resolves placeholders and parses one binary composition document.
|
||||
pub fn read_composition_json_file_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<CompositionConfigDocument> {
|
||||
) -> ks_core::Result<crate::CompositionConfigDocument> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -187,14 +187,14 @@ pub fn read_composition_json_file_with_environment(
|
||||
));
|
||||
},
|
||||
};
|
||||
return parse_composition_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
return crate::parse_composition_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
}
|
||||
|
||||
/// Returns one named binary composition profile.
|
||||
pub fn composition_profile<'a>(
|
||||
document: &'a CompositionConfigDocument,
|
||||
document: &'a crate::CompositionConfigDocument,
|
||||
profile_name: &str,
|
||||
) -> ks_core::Result<&'a CompositionProfileConfig> {
|
||||
) -> ks_core::Result<&'a crate::CompositionProfileConfig> {
|
||||
for profile in &document.profiles {
|
||||
if profile.name == profile_name {
|
||||
return std::result::Result::Ok(profile);
|
||||
@@ -208,9 +208,9 @@ pub fn composition_profile<'a>(
|
||||
|
||||
/// Returns the active binary composition profile.
|
||||
pub fn active_composition_profile(
|
||||
document: &CompositionConfigDocument,
|
||||
) -> ks_core::Result<&CompositionProfileConfig> {
|
||||
return composition_profile(document, document.active_profile.as_str());
|
||||
document: &crate::CompositionConfigDocument,
|
||||
) -> ks_core::Result<&crate::CompositionProfileConfig> {
|
||||
return crate::composition_profile(document, document.active_profile.as_str());
|
||||
}
|
||||
|
||||
/// Resolves a source path relative to the workspace root unless already absolute.
|
||||
@@ -238,7 +238,7 @@ pub fn resolve_profile_selection<'a>(
|
||||
|
||||
/// Resolves all shared source selections into the runtime application contract.
|
||||
pub fn compose_app_config(
|
||||
document: &CompositionConfigDocument,
|
||||
document: &crate::CompositionConfigDocument,
|
||||
transport: &crate::TransportConfigDocument,
|
||||
listeners: &crate::ListenersConfigDocument,
|
||||
store: &crate::StoreConfigDocument,
|
||||
@@ -247,23 +247,23 @@ pub fn compose_app_config(
|
||||
) -> ks_core::Result<crate::AppConfig> {
|
||||
let mut profiles = std::vec::Vec::<crate::ProfileConfig>::new();
|
||||
for profile in &document.profiles {
|
||||
let transport_name = resolve_profile_selection(
|
||||
let transport_name = crate::resolve_profile_selection(
|
||||
profile.transport_profile.as_deref(),
|
||||
transport.default_profile.as_str(),
|
||||
);
|
||||
let listeners_name = resolve_profile_selection(
|
||||
let listeners_name = crate::resolve_profile_selection(
|
||||
profile.listeners_profile.as_deref(),
|
||||
listeners.default_profile.as_str(),
|
||||
);
|
||||
let store_name = resolve_profile_selection(
|
||||
let store_name = crate::resolve_profile_selection(
|
||||
profile.store_profile.as_deref(),
|
||||
store.default_profile.as_str(),
|
||||
);
|
||||
let wallet_name = resolve_profile_selection(
|
||||
let wallet_name = crate::resolve_profile_selection(
|
||||
profile.wallet_profile.as_deref(),
|
||||
wallet.default_profile.as_str(),
|
||||
);
|
||||
let execution_name = resolve_profile_selection(
|
||||
let execution_name = crate::resolve_profile_selection(
|
||||
profile.execution_profile.as_deref(),
|
||||
execution.default_profile.as_str(),
|
||||
);
|
||||
@@ -313,24 +313,34 @@ pub fn compose_app_config(
|
||||
pub fn read_composed_app_config_with_environment(
|
||||
composition_path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<ComposedAppConfig> {
|
||||
let composition =
|
||||
match read_composition_json_file_with_environment(composition_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let logging_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.logging.as_str());
|
||||
let transport_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.transport.as_str());
|
||||
let listeners_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.listeners.as_str());
|
||||
) -> ks_core::Result<crate::ComposedAppConfig> {
|
||||
let composition = match crate::read_composition_json_file_with_environment(
|
||||
composition_path,
|
||||
workspace_root,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let logging_path = crate::resolve_composition_source_path(
|
||||
workspace_root,
|
||||
composition.sources.logging.as_str(),
|
||||
);
|
||||
let transport_path = crate::resolve_composition_source_path(
|
||||
workspace_root,
|
||||
composition.sources.transport.as_str(),
|
||||
);
|
||||
let listeners_path = crate::resolve_composition_source_path(
|
||||
workspace_root,
|
||||
composition.sources.listeners.as_str(),
|
||||
);
|
||||
let store_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.store.as_str());
|
||||
crate::resolve_composition_source_path(workspace_root, composition.sources.store.as_str());
|
||||
let wallet_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.wallet.as_str());
|
||||
let execution_path =
|
||||
resolve_composition_source_path(workspace_root, composition.sources.execution.as_str());
|
||||
crate::resolve_composition_source_path(workspace_root, composition.sources.wallet.as_str());
|
||||
let execution_path = crate::resolve_composition_source_path(
|
||||
workspace_root,
|
||||
composition.sources.execution.as_str(),
|
||||
);
|
||||
let transport =
|
||||
match crate::read_transport_json_file_with_environment(&transport_path, workspace_root) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -359,13 +369,18 @@ pub fn read_composed_app_config_with_environment(
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let app_config =
|
||||
match compose_app_config(&composition, &transport, &listeners, &store, &wallet, &execution)
|
||||
{
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(ComposedAppConfig {
|
||||
let app_config = match crate::compose_app_config(
|
||||
&composition,
|
||||
&transport,
|
||||
&listeners,
|
||||
&store,
|
||||
&wallet,
|
||||
&execution,
|
||||
) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return std::result::Result::Ok(crate::ComposedAppConfig {
|
||||
composition,
|
||||
app_config,
|
||||
logging_path,
|
||||
@@ -421,9 +436,9 @@ pub fn read_default_shared_app_config_with_environment(
|
||||
} else {
|
||||
"shared_default".to_string()
|
||||
};
|
||||
let composition = CompositionConfigDocument {
|
||||
let composition = crate::CompositionConfigDocument {
|
||||
active_profile: profile_name.clone(),
|
||||
sources: CompositionConfigSources {
|
||||
sources: crate::CompositionConfigSources {
|
||||
logging: DEFAULT_LOGGING_CONFIG_PATH.to_string(),
|
||||
transport: DEFAULT_TRANSPORT_CONFIG_PATH.to_string(),
|
||||
listeners: DEFAULT_LISTENERS_CONFIG_PATH.to_string(),
|
||||
@@ -431,7 +446,7 @@ pub fn read_default_shared_app_config_with_environment(
|
||||
wallet: DEFAULT_WALLET_CONFIG_PATH.to_string(),
|
||||
execution: DEFAULT_EXECUTION_CONFIG_PATH.to_string(),
|
||||
},
|
||||
profiles: vec![CompositionProfileConfig {
|
||||
profiles: vec![crate::CompositionProfileConfig {
|
||||
name: profile_name.clone(),
|
||||
application: std::option::Option::None,
|
||||
logging_profile: std::option::Option::None,
|
||||
@@ -442,11 +457,20 @@ pub fn read_default_shared_app_config_with_environment(
|
||||
execution_profile: std::option::Option::None,
|
||||
}],
|
||||
};
|
||||
return compose_app_config(&composition, &transport, &listeners, &store, &wallet, &execution);
|
||||
return crate::compose_app_config(
|
||||
&composition,
|
||||
&transport,
|
||||
&listeners,
|
||||
&store,
|
||||
&wallet,
|
||||
&execution,
|
||||
);
|
||||
}
|
||||
|
||||
/// Validates a typed binary composition document before referenced documents are loaded.
|
||||
pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks_core::Result<()> {
|
||||
pub fn validate_composition_document(
|
||||
document: &crate::CompositionConfigDocument,
|
||||
) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&document.active_profile, "composition.active_profile") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -498,7 +522,7 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks
|
||||
));
|
||||
}
|
||||
}
|
||||
return match active_composition_profile(document) {
|
||||
return match crate::active_composition_profile(document) {
|
||||
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -507,7 +531,7 @@ pub fn validate_composition_document(document: &CompositionConfigDocument) -> ks
|
||||
fn validate_logging_references_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
composition: &CompositionConfigDocument,
|
||||
composition: &crate::CompositionConfigDocument,
|
||||
) -> ks_core::Result<()> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
@@ -566,7 +590,7 @@ fn validate_logging_references_with_environment(
|
||||
}
|
||||
for profile in &composition.profiles {
|
||||
let selected =
|
||||
resolve_profile_selection(profile.logging_profile.as_deref(), default_profile);
|
||||
crate::resolve_profile_selection(profile.logging_profile.as_deref(), default_profile);
|
||||
if !names.contains(selected) {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"composition_logging_profile_not_found",
|
||||
@@ -586,17 +610,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn default_and_example_compositions_validate() {
|
||||
assert!(super::parse_composition_json(DEFAULT_COMPOSITION).is_ok());
|
||||
assert!(super::parse_composition_json(EXAMPLE_COMPOSITION).is_ok());
|
||||
assert!(crate::parse_composition_json(DEFAULT_COMPOSITION).is_ok());
|
||||
assert!(crate::parse_composition_json(EXAMPLE_COMPOSITION).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_devnet_composition_uses_shared_defaults() {
|
||||
let composition = match super::parse_composition_json(DEFAULT_COMPOSITION) {
|
||||
let composition = match crate::parse_composition_json(DEFAULT_COMPOSITION) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("default composition must parse: {error}"),
|
||||
};
|
||||
let profile = match super::composition_profile(&composition, "local_devnet") {
|
||||
let profile = match crate::composition_profile(&composition, "local_devnet") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
panic!("local Devnet composition must resolve: {error}")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/environment.rs
|
||||
// version: 6
|
||||
// version: 8
|
||||
|
||||
//! Environment-file loading and configuration placeholder resolution.
|
||||
|
||||
@@ -16,7 +16,7 @@ pub struct EnvironmentLoadReport {
|
||||
/// fallback text declared with `${NAME:-fallback}` placeholders.
|
||||
pub fn load_workspace_environment(
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<EnvironmentLoadReport> {
|
||||
) -> ks_core::Result<crate::EnvironmentLoadReport> {
|
||||
let explicit_path = std::env::var("KS_ENV_FILE").ok();
|
||||
let selected_path = match explicit_path {
|
||||
std::option::Option::Some(path) if !path.trim().is_empty() => {
|
||||
@@ -26,10 +26,10 @@ pub fn load_workspace_environment(
|
||||
_ => workspace_root.join(".env"),
|
||||
};
|
||||
if !selected_path.exists() {
|
||||
return std::result::Result::Ok(EnvironmentLoadReport { loaded_path: None });
|
||||
return std::result::Result::Ok(crate::EnvironmentLoadReport { loaded_path: None });
|
||||
}
|
||||
return match dotenvy::from_path(&selected_path) {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(EnvironmentLoadReport {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(crate::EnvironmentLoadReport {
|
||||
loaded_path: std::option::Option::Some(selected_path),
|
||||
}),
|
||||
std::result::Result::Err(_) => std::result::Result::Err(ks_core::Error::new(
|
||||
@@ -82,14 +82,14 @@ mod tests {
|
||||
#[test]
|
||||
fn fallback_is_used_when_variable_is_absent() {
|
||||
let resolved =
|
||||
super::resolve_environment_placeholders("${KS_CONFIG_TEST_MISSING:-fallback}");
|
||||
crate::resolve_environment_placeholders("${KS_CONFIG_TEST_MISSING:-fallback}");
|
||||
assert_eq!(resolved, "fallback");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unresolved_required_placeholder_is_preserved() {
|
||||
let resolved =
|
||||
super::resolve_environment_placeholders("prefix-${KS_CONFIG_TEST_MISSING}-suffix");
|
||||
crate::resolve_environment_placeholders("prefix-${KS_CONFIG_TEST_MISSING}-suffix");
|
||||
assert_eq!(resolved, "prefix-${KS_CONFIG_TEST_MISSING}-suffix");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/execution.rs
|
||||
// version: 3
|
||||
// version: 5
|
||||
|
||||
//! Independent execution policy configuration document loading and validation.
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct ExecutionConfigDocument {
|
||||
/// Default execution profile used when a composition does not override it.
|
||||
pub default_profile: std::string::String,
|
||||
/// Named execution policy profiles.
|
||||
pub profiles: std::vec::Vec<ExecutionProfileConfig>,
|
||||
pub profiles: std::vec::Vec<crate::ExecutionProfileConfig>,
|
||||
}
|
||||
|
||||
/// Named execution policy profile.
|
||||
@@ -31,7 +31,7 @@ pub fn execution_json_schema_text() -> &'static str {
|
||||
}
|
||||
|
||||
/// Parses and validates one execution configuration document.
|
||||
pub fn parse_execution_json(raw_json: &str) -> ks_core::Result<ExecutionConfigDocument> {
|
||||
pub fn parse_execution_json(raw_json: &str) -> ks_core::Result<crate::ExecutionConfigDocument> {
|
||||
let schema = match serde_json::from_str::<serde_json::Value>(EXECUTION_JSON_SCHEMA) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
@@ -65,7 +65,7 @@ pub fn parse_execution_json(raw_json: &str) -> ks_core::Result<ExecutionConfigDo
|
||||
"execution configuration does not satisfy its schema",
|
||||
));
|
||||
}
|
||||
let document = match serde_json::from_str::<ExecutionConfigDocument>(raw_json) {
|
||||
let document = match serde_json::from_str::<crate::ExecutionConfigDocument>(raw_json) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
@@ -74,7 +74,7 @@ pub fn parse_execution_json(raw_json: &str) -> ks_core::Result<ExecutionConfigDo
|
||||
));
|
||||
},
|
||||
};
|
||||
return match validate_execution_document(&document) {
|
||||
return match crate::validate_execution_document(&document) {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(document),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -84,7 +84,7 @@ pub fn parse_execution_json(raw_json: &str) -> ks_core::Result<ExecutionConfigDo
|
||||
pub fn read_execution_json_file_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<ExecutionConfigDocument> {
|
||||
) -> ks_core::Result<crate::ExecutionConfigDocument> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -98,14 +98,14 @@ pub fn read_execution_json_file_with_environment(
|
||||
));
|
||||
},
|
||||
};
|
||||
return parse_execution_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
return crate::parse_execution_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
}
|
||||
|
||||
/// Returns one named execution profile.
|
||||
pub fn execution_profile<'a>(
|
||||
document: &'a ExecutionConfigDocument,
|
||||
document: &'a crate::ExecutionConfigDocument,
|
||||
profile_name: &str,
|
||||
) -> ks_core::Result<&'a ExecutionProfileConfig> {
|
||||
) -> ks_core::Result<&'a crate::ExecutionProfileConfig> {
|
||||
for profile in &document.profiles {
|
||||
if profile.name == profile_name {
|
||||
return std::result::Result::Ok(profile);
|
||||
@@ -119,13 +119,15 @@ pub fn execution_profile<'a>(
|
||||
|
||||
/// Returns the default execution profile.
|
||||
pub fn default_execution_profile(
|
||||
document: &ExecutionConfigDocument,
|
||||
) -> ks_core::Result<&ExecutionProfileConfig> {
|
||||
return execution_profile(document, document.default_profile.as_str());
|
||||
document: &crate::ExecutionConfigDocument,
|
||||
) -> ks_core::Result<&crate::ExecutionProfileConfig> {
|
||||
return crate::execution_profile(document, document.default_profile.as_str());
|
||||
}
|
||||
|
||||
/// Validates one typed execution configuration document.
|
||||
pub fn validate_execution_document(document: &ExecutionConfigDocument) -> ks_core::Result<()> {
|
||||
pub fn validate_execution_document(
|
||||
document: &crate::ExecutionConfigDocument,
|
||||
) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&document.default_profile, "execution.default_profile") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -153,7 +155,7 @@ pub fn validate_execution_document(document: &ExecutionConfigDocument) -> ks_cor
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return match default_execution_profile(document) {
|
||||
return match crate::default_execution_profile(document) {
|
||||
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -167,7 +169,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn default_and_example_execution_configs_validate() {
|
||||
assert!(super::parse_execution_json(DEFAULT_EXECUTION).is_ok());
|
||||
assert!(super::parse_execution_json(EXAMPLE_EXECUTION).is_ok());
|
||||
assert!(crate::parse_execution_json(DEFAULT_EXECUTION).is_ok());
|
||||
assert!(crate::parse_execution_json(EXAMPLE_EXECUTION).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/lib.rs
|
||||
// version: 10
|
||||
// version: 11
|
||||
|
||||
//! Khadhroony Solana shared configuration contracts and composition helpers.
|
||||
#![warn(missing_docs)]
|
||||
@@ -125,16 +125,12 @@ pub use self::settings::HttpEndpointConfig;
|
||||
pub use self::settings::ListenerConfig;
|
||||
/// Exposes the log listener configuration type.
|
||||
pub use self::settings::LogListenerConfig;
|
||||
/// Exposes the PostgreSQL configuration type.
|
||||
pub use self::settings::PostgresConfig;
|
||||
/// Exposes the profile configuration type.
|
||||
pub use self::settings::ProfileConfig;
|
||||
/// Exposes the program listener configuration type.
|
||||
pub use self::settings::ProgramListenerConfig;
|
||||
/// Exposes the Solana configuration type.
|
||||
pub use self::settings::SolanaConfig;
|
||||
/// Exposes the SQLite configuration type.
|
||||
pub use self::settings::SqliteConfig;
|
||||
/// Exposes the wallet configuration type.
|
||||
pub use self::settings::WalletConfig;
|
||||
/// Exposes the WebSocket endpoint configuration type.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/listeners.rs
|
||||
// version: 3
|
||||
// version: 5
|
||||
|
||||
//! Independent Solana listener configuration document loading and validation.
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct ListenersConfigDocument {
|
||||
/// Default listener profile used by consumers that do not provide an explicit selection.
|
||||
pub default_profile: std::string::String,
|
||||
/// Named listener profiles available in this document.
|
||||
pub profiles: std::vec::Vec<ListenersProfileConfig>,
|
||||
pub profiles: std::vec::Vec<crate::ListenersProfileConfig>,
|
||||
}
|
||||
|
||||
/// Named listener profile.
|
||||
@@ -51,7 +51,7 @@ pub fn listeners_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||
|
||||
/// Validates raw listener JSON against the embedded listener schema.
|
||||
pub fn validate_listeners_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
let schema = match listeners_json_schema_value() {
|
||||
let schema = match crate::listeners_json_schema_value() {
|
||||
std::result::Result::Ok(schema) => schema,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -83,12 +83,12 @@ pub fn validate_listeners_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
}
|
||||
|
||||
/// Parses and validates one independent listener configuration document.
|
||||
pub fn parse_listeners_json(raw_json: &str) -> ks_core::Result<ListenersConfigDocument> {
|
||||
match validate_listeners_json_schema(raw_json) {
|
||||
pub fn parse_listeners_json(raw_json: &str) -> ks_core::Result<crate::ListenersConfigDocument> {
|
||||
match crate::validate_listeners_json_schema(raw_json) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let document = match serde_json::from_str::<ListenersConfigDocument>(raw_json) {
|
||||
let document = match serde_json::from_str::<crate::ListenersConfigDocument>(raw_json) {
|
||||
std::result::Result::Ok(document) => document,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
@@ -97,7 +97,7 @@ pub fn parse_listeners_json(raw_json: &str) -> ks_core::Result<ListenersConfigDo
|
||||
));
|
||||
},
|
||||
};
|
||||
return match validate_listeners_document(&document) {
|
||||
return match crate::validate_listeners_document(&document) {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(document),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -106,7 +106,7 @@ pub fn parse_listeners_json(raw_json: &str) -> ks_core::Result<ListenersConfigDo
|
||||
/// Reads and parses one listener configuration document from a filesystem path.
|
||||
pub fn read_listeners_json_file(
|
||||
path: &std::path::Path,
|
||||
) -> ks_core::Result<ListenersConfigDocument> {
|
||||
) -> ks_core::Result<crate::ListenersConfigDocument> {
|
||||
let raw_json = match std::fs::read_to_string(path) {
|
||||
std::result::Result::Ok(content) => content,
|
||||
std::result::Result::Err(error) => {
|
||||
@@ -116,14 +116,14 @@ pub fn read_listeners_json_file(
|
||||
));
|
||||
},
|
||||
};
|
||||
return parse_listeners_json(&raw_json);
|
||||
return crate::parse_listeners_json(&raw_json);
|
||||
}
|
||||
|
||||
/// Loads workspace environment values, resolves placeholders and parses listener configuration.
|
||||
pub fn read_listeners_json_file_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<ListenersConfigDocument> {
|
||||
) -> ks_core::Result<crate::ListenersConfigDocument> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -138,14 +138,14 @@ pub fn read_listeners_json_file_with_environment(
|
||||
},
|
||||
};
|
||||
let resolved = crate::resolve_environment_placeholders(&raw_json);
|
||||
return parse_listeners_json(&resolved);
|
||||
return crate::parse_listeners_json(&resolved);
|
||||
}
|
||||
|
||||
/// Returns one named listener profile.
|
||||
pub fn listeners_profile<'a>(
|
||||
document: &'a ListenersConfigDocument,
|
||||
document: &'a crate::ListenersConfigDocument,
|
||||
profile_name: &str,
|
||||
) -> ks_core::Result<&'a ListenersProfileConfig> {
|
||||
) -> ks_core::Result<&'a crate::ListenersProfileConfig> {
|
||||
for profile in &document.profiles {
|
||||
if profile.name == profile_name {
|
||||
return std::result::Result::Ok(profile);
|
||||
@@ -159,13 +159,13 @@ pub fn listeners_profile<'a>(
|
||||
|
||||
/// Returns the default listener profile declared by the document.
|
||||
pub fn default_listeners_profile(
|
||||
document: &ListenersConfigDocument,
|
||||
) -> ks_core::Result<&ListenersProfileConfig> {
|
||||
return listeners_profile(document, document.default_profile.as_str());
|
||||
document: &crate::ListenersConfigDocument,
|
||||
) -> ks_core::Result<&crate::ListenersProfileConfig> {
|
||||
return crate::listeners_profile(document, document.default_profile.as_str());
|
||||
}
|
||||
|
||||
/// Converts one named listener profile into the resolved runtime listener contract.
|
||||
pub fn resolved_listener_config(profile: &ListenersProfileConfig) -> crate::ListenerConfig {
|
||||
pub fn resolved_listener_config(profile: &crate::ListenersProfileConfig) -> crate::ListenerConfig {
|
||||
return crate::ListenerConfig {
|
||||
enabled: profile.enabled,
|
||||
default_commitment: profile.default_commitment.clone(),
|
||||
@@ -176,7 +176,9 @@ pub fn resolved_listener_config(profile: &ListenersProfileConfig) -> crate::List
|
||||
}
|
||||
|
||||
/// Validates a typed listener configuration document.
|
||||
pub fn validate_listeners_document(document: &ListenersConfigDocument) -> ks_core::Result<()> {
|
||||
pub fn validate_listeners_document(
|
||||
document: &crate::ListenersConfigDocument,
|
||||
) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&document.default_profile, "default_profile") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -199,13 +201,13 @@ pub fn validate_listeners_document(document: &ListenersConfigDocument) -> ks_cor
|
||||
profile.name.clone(),
|
||||
));
|
||||
}
|
||||
let resolved = resolved_listener_config(profile);
|
||||
let resolved = crate::resolved_listener_config(profile);
|
||||
match crate::validate_listeners(&resolved) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return match default_listeners_profile(document) {
|
||||
return match crate::default_listeners_profile(document) {
|
||||
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -219,7 +221,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn default_and_example_listener_configs_validate() {
|
||||
assert!(super::parse_listeners_json(DEFAULT_LISTENERS).is_ok());
|
||||
assert!(super::parse_listeners_json(EXAMPLE_LISTENERS).is_ok());
|
||||
assert!(crate::parse_listeners_json(DEFAULT_LISTENERS).is_ok());
|
||||
assert!(crate::parse_listeners_json(EXAMPLE_LISTENERS).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/schema.rs
|
||||
// version: 1
|
||||
// version: 2
|
||||
|
||||
//! Generic JSON Schema validation helpers for binary-owned configuration fragments.
|
||||
|
||||
@@ -42,7 +42,7 @@ mod tests {
|
||||
fn validation_error_never_echoes_instance_secret() {
|
||||
let schema = r#"{"type":"object","additionalProperties":false,"required":["value"],"properties":{"value":{"type":"integer"}}}"#;
|
||||
let instance = serde_json::json!({"value": "SECRET-CANARY"});
|
||||
let result = super::validate_json_value_against_schema(
|
||||
let result = crate::validate_json_value_against_schema(
|
||||
schema,
|
||||
&instance,
|
||||
"test_fragment_validation_failed",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/sensitivity.rs
|
||||
// version: 1
|
||||
// version: 3
|
||||
|
||||
//! Application environment-variable sensitivity classification.
|
||||
|
||||
@@ -20,7 +20,7 @@ pub enum EnvironmentValueSensitivity {
|
||||
/// one `KS_SECRET_*` placeholder is classified as secret even when the surrounding URL is public.
|
||||
pub fn classify_environment_template(
|
||||
raw: &str,
|
||||
) -> std::option::Option<EnvironmentValueSensitivity> {
|
||||
) -> std::option::Option<crate::EnvironmentValueSensitivity> {
|
||||
let bytes = raw.as_bytes();
|
||||
let mut sensitivity = std::option::Option::None;
|
||||
let mut index = 0_usize;
|
||||
@@ -36,7 +36,8 @@ pub fn classify_environment_template(
|
||||
std::option::Option::Some((name, _)) => name,
|
||||
std::option::Option::None => expression,
|
||||
};
|
||||
sensitivity = merge_sensitivity(sensitivity, classify_environment_variable(name));
|
||||
sensitivity =
|
||||
merge_sensitivity(sensitivity, crate::classify_environment_variable(name));
|
||||
index = end + 1;
|
||||
continue;
|
||||
}
|
||||
@@ -49,15 +50,15 @@ pub fn classify_environment_template(
|
||||
/// Classifies a Khadhroony-owned environment variable by its namespace.
|
||||
pub fn classify_environment_variable(
|
||||
name: &str,
|
||||
) -> std::option::Option<EnvironmentValueSensitivity> {
|
||||
) -> std::option::Option<crate::EnvironmentValueSensitivity> {
|
||||
if name.starts_with("KS_SECRET_") || name.starts_with("KB_SECRET_") {
|
||||
return std::option::Option::Some(EnvironmentValueSensitivity::Secret);
|
||||
return std::option::Option::Some(crate::EnvironmentValueSensitivity::Secret);
|
||||
}
|
||||
if name.starts_with("KS_PUBLIC_") || name.starts_with("KB_PUBLIC_") {
|
||||
return std::option::Option::Some(EnvironmentValueSensitivity::Public);
|
||||
return std::option::Option::Some(crate::EnvironmentValueSensitivity::Public);
|
||||
}
|
||||
if name.starts_with("KS_") || name.starts_with("KB_") {
|
||||
return std::option::Option::Some(EnvironmentValueSensitivity::Internal);
|
||||
return std::option::Option::Some(crate::EnvironmentValueSensitivity::Internal);
|
||||
}
|
||||
return std::option::Option::None;
|
||||
}
|
||||
@@ -67,14 +68,14 @@ pub fn diagnostic_environment_value(
|
||||
name: &str,
|
||||
value: &str,
|
||||
) -> std::option::Option<std::string::String> {
|
||||
return match classify_environment_variable(name) {
|
||||
std::option::Option::Some(EnvironmentValueSensitivity::Secret) => {
|
||||
return match crate::classify_environment_variable(name) {
|
||||
std::option::Option::Some(crate::EnvironmentValueSensitivity::Secret) => {
|
||||
std::option::Option::Some("<redacted>".to_string())
|
||||
},
|
||||
std::option::Option::Some(EnvironmentValueSensitivity::Internal) => {
|
||||
std::option::Option::Some(crate::EnvironmentValueSensitivity::Internal) => {
|
||||
std::option::Option::Some("<configured>".to_string())
|
||||
},
|
||||
std::option::Option::Some(EnvironmentValueSensitivity::Public) => {
|
||||
std::option::Option::Some(crate::EnvironmentValueSensitivity::Public) => {
|
||||
std::option::Option::Some(value.to_string())
|
||||
},
|
||||
std::option::Option::None => std::option::Option::None,
|
||||
@@ -82,23 +83,23 @@ pub fn diagnostic_environment_value(
|
||||
}
|
||||
|
||||
fn merge_sensitivity(
|
||||
left: std::option::Option<EnvironmentValueSensitivity>,
|
||||
right: std::option::Option<EnvironmentValueSensitivity>,
|
||||
) -> std::option::Option<EnvironmentValueSensitivity> {
|
||||
if left == std::option::Option::Some(EnvironmentValueSensitivity::Secret)
|
||||
|| right == std::option::Option::Some(EnvironmentValueSensitivity::Secret)
|
||||
left: std::option::Option<crate::EnvironmentValueSensitivity>,
|
||||
right: std::option::Option<crate::EnvironmentValueSensitivity>,
|
||||
) -> std::option::Option<crate::EnvironmentValueSensitivity> {
|
||||
if left == std::option::Option::Some(crate::EnvironmentValueSensitivity::Secret)
|
||||
|| right == std::option::Option::Some(crate::EnvironmentValueSensitivity::Secret)
|
||||
{
|
||||
return std::option::Option::Some(EnvironmentValueSensitivity::Secret);
|
||||
return std::option::Option::Some(crate::EnvironmentValueSensitivity::Secret);
|
||||
}
|
||||
if left == std::option::Option::Some(EnvironmentValueSensitivity::Internal)
|
||||
|| right == std::option::Option::Some(EnvironmentValueSensitivity::Internal)
|
||||
if left == std::option::Option::Some(crate::EnvironmentValueSensitivity::Internal)
|
||||
|| right == std::option::Option::Some(crate::EnvironmentValueSensitivity::Internal)
|
||||
{
|
||||
return std::option::Option::Some(EnvironmentValueSensitivity::Internal);
|
||||
return std::option::Option::Some(crate::EnvironmentValueSensitivity::Internal);
|
||||
}
|
||||
if left == std::option::Option::Some(EnvironmentValueSensitivity::Public)
|
||||
|| right == std::option::Option::Some(EnvironmentValueSensitivity::Public)
|
||||
if left == std::option::Option::Some(crate::EnvironmentValueSensitivity::Public)
|
||||
|| right == std::option::Option::Some(crate::EnvironmentValueSensitivity::Public)
|
||||
{
|
||||
return std::option::Option::Some(EnvironmentValueSensitivity::Public);
|
||||
return std::option::Option::Some(crate::EnvironmentValueSensitivity::Public);
|
||||
}
|
||||
return std::option::Option::None;
|
||||
}
|
||||
@@ -108,19 +109,19 @@ mod tests {
|
||||
#[test]
|
||||
fn composed_values_inherit_the_strongest_placeholder_sensitivity() {
|
||||
assert_eq!(
|
||||
super::classify_environment_template(
|
||||
crate::classify_environment_template(
|
||||
"https://rpc.invalid/?api-key=${KS_SECRET_HELIUS_API_KEY}&mint=${KS_PUBLIC_DEVNET_TOKEN_2022_MINT}"
|
||||
),
|
||||
std::option::Option::Some(super::EnvironmentValueSensitivity::Secret)
|
||||
);
|
||||
assert_eq!(
|
||||
super::classify_environment_template(
|
||||
crate::classify_environment_template(
|
||||
"${KS_LOGS_DIRECTORY:-logs}/${KS_PUBLIC_SPL_TOKEN_PROGRAM_ID}"
|
||||
),
|
||||
std::option::Option::Some(super::EnvironmentValueSensitivity::Internal)
|
||||
);
|
||||
assert_eq!(
|
||||
super::classify_environment_template("${KS_PUBLIC_SPL_TOKEN_PROGRAM_ID}"),
|
||||
crate::classify_environment_template("${KS_PUBLIC_SPL_TOKEN_PROGRAM_ID}"),
|
||||
std::option::Option::Some(super::EnvironmentValueSensitivity::Public)
|
||||
);
|
||||
}
|
||||
@@ -128,15 +129,15 @@ mod tests {
|
||||
#[test]
|
||||
fn generic_diagnostic_never_reveals_secret_or_internal_values() {
|
||||
assert_eq!(
|
||||
super::diagnostic_environment_value("KS_SECRET_HELIUS_API_KEY", "SECRET-CANARY"),
|
||||
crate::diagnostic_environment_value("KS_SECRET_HELIUS_API_KEY", "SECRET-CANARY"),
|
||||
std::option::Option::Some("<redacted>".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
super::diagnostic_environment_value("KS_LOGS_DIRECTORY", "/private/logs"),
|
||||
crate::diagnostic_environment_value("KS_LOGS_DIRECTORY", "/private/logs"),
|
||||
std::option::Option::Some("<configured>".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
super::diagnostic_environment_value("KS_PUBLIC_SPL_TOKEN_PROGRAM_ID", "Token111"),
|
||||
crate::diagnostic_environment_value("KS_PUBLIC_SPL_TOKEN_PROGRAM_ID", "Token111"),
|
||||
std::option::Option::Some("Token111".to_string())
|
||||
);
|
||||
}
|
||||
@@ -144,17 +145,17 @@ mod tests {
|
||||
#[test]
|
||||
fn namespaces_map_to_secret_internal_and_public() {
|
||||
assert_eq!(
|
||||
super::classify_environment_variable("KS_SECRET_HELIUS_API_KEY"),
|
||||
crate::classify_environment_variable("KS_SECRET_HELIUS_API_KEY"),
|
||||
std::option::Option::Some(super::EnvironmentValueSensitivity::Secret)
|
||||
);
|
||||
assert_eq!(
|
||||
super::classify_environment_variable("KS_LOGS_DIRECTORY"),
|
||||
crate::classify_environment_variable("KS_LOGS_DIRECTORY"),
|
||||
std::option::Option::Some(super::EnvironmentValueSensitivity::Internal)
|
||||
);
|
||||
assert_eq!(
|
||||
super::classify_environment_variable("KB_PUBLIC_DEMO_VALUE"),
|
||||
crate::classify_environment_variable("KB_PUBLIC_DEMO_VALUE"),
|
||||
std::option::Option::Some(super::EnvironmentValueSensitivity::Public)
|
||||
);
|
||||
assert_eq!(super::classify_environment_variable("HOME"), std::option::Option::None);
|
||||
assert_eq!(crate::classify_environment_variable("HOME"), std::option::Option::None);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/settings.rs
|
||||
// version: 25
|
||||
// version: 29
|
||||
|
||||
//! Resolved runtime configuration models retained during the `0.5.1` source-document split.
|
||||
|
||||
@@ -12,7 +12,7 @@ pub struct AppConfig {
|
||||
/// Active profile name.
|
||||
pub active_profile: std::string::String,
|
||||
/// Named profiles reconstructed from the active binary composition and shared documents.
|
||||
pub profiles: std::vec::Vec<ProfileConfig>,
|
||||
pub profiles: std::vec::Vec<crate::ProfileConfig>,
|
||||
}
|
||||
|
||||
/// Resolved runtime profile selected by the root active profile name.
|
||||
@@ -21,67 +21,35 @@ pub struct ProfileConfig {
|
||||
/// Profile code.
|
||||
pub name: std::string::String,
|
||||
/// Database configuration.
|
||||
pub database: DatabaseConfig,
|
||||
pub database: crate::DatabaseConfig,
|
||||
/// Solana endpoint and listener configuration.
|
||||
pub solana: SolanaConfig,
|
||||
pub solana: crate::SolanaConfig,
|
||||
/// Wallet configuration.
|
||||
pub wallet: WalletConfig,
|
||||
pub wallet: crate::WalletConfig,
|
||||
/// Execution safety configuration.
|
||||
pub execution: ExecutionConfig,
|
||||
pub execution: crate::ExecutionConfig,
|
||||
}
|
||||
|
||||
/// Database backend configuration.
|
||||
/// Backend-agnostic store configuration resolved by `ks-config`.
|
||||
#[derive(Clone, serde::Deserialize, Eq, PartialEq)]
|
||||
pub struct DatabaseConfig {
|
||||
/// Enables database storage.
|
||||
/// Enables persistent storage for this profile.
|
||||
pub enabled: bool,
|
||||
/// Selected backend code.
|
||||
/// Selected backend code interpreted by `ks-store`.
|
||||
pub backend: std::string::String,
|
||||
/// PostgreSQL configuration.
|
||||
pub postgres: PostgresConfig,
|
||||
/// SQLite configuration for tests or imports.
|
||||
pub sqlite: SqliteConfig,
|
||||
}
|
||||
|
||||
/// PostgreSQL backend configuration.
|
||||
#[derive(Clone, serde::Deserialize, Eq, PartialEq)]
|
||||
pub struct PostgresConfig {
|
||||
/// PostgreSQL URL or local placeholder.
|
||||
pub url: std::string::String,
|
||||
/// Maximum connection count.
|
||||
pub max_connections: u32,
|
||||
/// Connection timeout in milliseconds.
|
||||
pub connect_timeout_ms: u64,
|
||||
/// Enables schema initialization at startup.
|
||||
pub auto_initialize_schema: bool,
|
||||
}
|
||||
|
||||
/// SQLite backend configuration.
|
||||
#[derive(Clone, serde::Deserialize, Eq, PartialEq)]
|
||||
pub struct SqliteConfig {
|
||||
/// SQLite database path.
|
||||
pub path: std::string::String,
|
||||
/// Creates the file when missing.
|
||||
pub create_if_missing: bool,
|
||||
/// Busy timeout in milliseconds.
|
||||
pub busy_timeout_ms: u64,
|
||||
/// Maximum connection count.
|
||||
pub max_connections: u32,
|
||||
/// Enables schema initialization at startup.
|
||||
pub auto_initialize_schema: bool,
|
||||
/// Enables WAL mode.
|
||||
pub use_wal: bool,
|
||||
/// Backend-specific options retained as an opaque JSON object until `ks-store` opens the store.
|
||||
pub backend_options: serde_json::Value,
|
||||
}
|
||||
|
||||
/// Solana endpoints and listener configuration.
|
||||
#[derive(Clone, serde::Deserialize, Eq, PartialEq)]
|
||||
pub struct SolanaConfig {
|
||||
/// HTTP JSON-RPC endpoints.
|
||||
pub http_endpoints: std::vec::Vec<HttpEndpointConfig>,
|
||||
pub http_endpoints: std::vec::Vec<crate::HttpEndpointConfig>,
|
||||
/// Standard Solana WebSocket endpoints.
|
||||
pub ws_endpoints: std::vec::Vec<WsEndpointConfig>,
|
||||
pub ws_endpoints: std::vec::Vec<crate::WsEndpointConfig>,
|
||||
/// Listener declarations.
|
||||
pub listeners: ListenerConfig,
|
||||
pub listeners: crate::ListenerConfig,
|
||||
}
|
||||
|
||||
/// HTTP JSON-RPC endpoint configuration.
|
||||
@@ -104,7 +72,7 @@ pub struct HttpEndpointConfig {
|
||||
/// Maximum idle connections per host.
|
||||
pub max_idle_connections_per_host: u32,
|
||||
/// Role-specific limits for this URL.
|
||||
pub roles: std::vec::Vec<EndpointRoleConfig>,
|
||||
pub roles: std::vec::Vec<crate::EndpointRoleConfig>,
|
||||
}
|
||||
|
||||
/// Standard Solana WebSocket endpoint configuration.
|
||||
@@ -133,7 +101,7 @@ pub struct WsEndpointConfig {
|
||||
/// Enables automatic reconnect for this endpoint.
|
||||
pub auto_reconnect: bool,
|
||||
/// Role-specific limits for this URL.
|
||||
pub roles: std::vec::Vec<EndpointRoleConfig>,
|
||||
pub roles: std::vec::Vec<crate::EndpointRoleConfig>,
|
||||
}
|
||||
|
||||
/// Role-specific endpoint limits.
|
||||
@@ -167,11 +135,11 @@ pub struct ListenerConfig {
|
||||
/// Default commitment used by listener subscriptions.
|
||||
pub default_commitment: std::string::String,
|
||||
/// Log listeners filtered by program mentions.
|
||||
pub log_listeners: std::vec::Vec<LogListenerConfig>,
|
||||
pub log_listeners: std::vec::Vec<crate::LogListenerConfig>,
|
||||
/// Program account listeners.
|
||||
pub program_listeners: std::vec::Vec<ProgramListenerConfig>,
|
||||
pub program_listeners: std::vec::Vec<crate::ProgramListenerConfig>,
|
||||
/// Account listeners.
|
||||
pub account_listeners: std::vec::Vec<AccountListenerConfig>,
|
||||
pub account_listeners: std::vec::Vec<crate::AccountListenerConfig>,
|
||||
}
|
||||
|
||||
/// Log listener filtered by a program identifier mention.
|
||||
@@ -298,7 +266,7 @@ pub fn config_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||
|
||||
/// Validates a raw resolved runtime JSON string against the embedded compatibility schema.
|
||||
pub fn validate_config_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
let schema = match config_json_schema_value() {
|
||||
let schema = match crate::config_json_schema_value() {
|
||||
std::result::Result::Ok(schema) => schema,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -331,12 +299,12 @@ pub fn validate_config_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
}
|
||||
|
||||
/// Parses the resolved runtime application contract from a JSON string and validates it.
|
||||
pub fn parse_config_json(raw_json: &str) -> ks_core::Result<AppConfig> {
|
||||
match validate_config_json_schema(raw_json) {
|
||||
pub fn parse_config_json(raw_json: &str) -> ks_core::Result<crate::AppConfig> {
|
||||
match crate::validate_config_json_schema(raw_json) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let config = match serde_json::from_str::<AppConfig>(raw_json) {
|
||||
let config = match serde_json::from_str::<crate::AppConfig>(raw_json) {
|
||||
std::result::Result::Ok(config) => config,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
@@ -345,14 +313,14 @@ pub fn parse_config_json(raw_json: &str) -> ks_core::Result<AppConfig> {
|
||||
));
|
||||
},
|
||||
};
|
||||
return match validate_config(&config) {
|
||||
return match crate::validate_config(&config) {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(config),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
|
||||
/// Reads and parses a resolved runtime application snapshot from a filesystem path.
|
||||
pub fn read_config_json_file(path: &std::path::Path) -> ks_core::Result<AppConfig> {
|
||||
pub fn read_config_json_file(path: &std::path::Path) -> ks_core::Result<crate::AppConfig> {
|
||||
let raw_json = match std::fs::read_to_string(path) {
|
||||
std::result::Result::Ok(content) => content,
|
||||
std::result::Result::Err(error) => {
|
||||
@@ -362,14 +330,14 @@ pub fn read_config_json_file(path: &std::path::Path) -> ks_core::Result<AppConfi
|
||||
));
|
||||
},
|
||||
};
|
||||
return parse_config_json(&raw_json);
|
||||
return crate::parse_config_json(&raw_json);
|
||||
}
|
||||
|
||||
/// Loads the workspace environment, resolves placeholders and parses one resolved runtime snapshot.
|
||||
pub fn read_config_json_file_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<AppConfig> {
|
||||
) -> ks_core::Result<crate::AppConfig> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -384,11 +352,11 @@ pub fn read_config_json_file_with_environment(
|
||||
},
|
||||
};
|
||||
let resolved = crate::resolve_environment_placeholders(&raw_json);
|
||||
return parse_config_json(&resolved);
|
||||
return crate::parse_config_json(&resolved);
|
||||
}
|
||||
|
||||
/// Returns the active profile declared by the root configuration.
|
||||
pub fn active_profile(config: &AppConfig) -> ks_core::Result<&ProfileConfig> {
|
||||
pub fn active_profile(config: &crate::AppConfig) -> ks_core::Result<&crate::ProfileConfig> {
|
||||
for profile in &config.profiles {
|
||||
if profile.name == config.active_profile {
|
||||
return std::result::Result::Ok(profile);
|
||||
@@ -401,7 +369,7 @@ pub fn active_profile(config: &AppConfig) -> ks_core::Result<&ProfileConfig> {
|
||||
}
|
||||
|
||||
/// Validates a typed configuration after JSON Schema validation and deserialization.
|
||||
pub fn validate_config(config: &AppConfig) -> ks_core::Result<()> {
|
||||
pub fn validate_config(config: &crate::AppConfig) -> ks_core::Result<()> {
|
||||
match validate_root_config(config) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -412,14 +380,14 @@ pub fn validate_config(config: &AppConfig) -> ks_core::Result<()> {
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return match active_profile(config) {
|
||||
return match crate::active_profile(config) {
|
||||
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
|
||||
fn validate_root_config(config: &AppConfig) -> ks_core::Result<()> {
|
||||
match require_non_empty(&config.active_profile, "active_profile") {
|
||||
fn validate_root_config(config: &crate::AppConfig) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&config.active_profile, "active_profile") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -432,7 +400,7 @@ fn validate_root_config(config: &AppConfig) -> ks_core::Result<()> {
|
||||
let mut names = std::collections::BTreeSet::<std::string::String>::new();
|
||||
let mut active_count = 0_u32;
|
||||
for profile in &config.profiles {
|
||||
match require_non_empty(&profile.name, "profile.name") {
|
||||
match crate::require_non_empty(&profile.name, "profile.name") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -455,8 +423,8 @@ fn validate_root_config(config: &AppConfig) -> ks_core::Result<()> {
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> {
|
||||
match validate_database(&profile.database) {
|
||||
fn validate_profile(profile: &crate::ProfileConfig) -> ks_core::Result<()> {
|
||||
match crate::validate_database(&profile.database) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -464,44 +432,32 @@ fn validate_profile(profile: &ProfileConfig) -> ks_core::Result<()> {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match validate_wallet(&profile.wallet) {
|
||||
match crate::validate_wallet(&profile.wallet) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match validate_execution(&profile.execution) {
|
||||
match crate::validate_execution(&profile.execution) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
return validate_wallet_execution_pair(&profile.wallet, &profile.execution);
|
||||
}
|
||||
|
||||
pub(crate) fn validate_database(config: &DatabaseConfig) -> ks_core::Result<()> {
|
||||
if config.backend != "postgres" && config.backend != "sqlite" {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"database_backend_invalid",
|
||||
config.backend.clone(),
|
||||
));
|
||||
}
|
||||
match require_non_empty(&config.postgres.url, "database.postgres.url") {
|
||||
pub(crate) fn validate_database(config: &crate::DatabaseConfig) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&config.backend, "database.backend") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
if config.postgres.max_connections == 0 || config.sqlite.max_connections == 0 {
|
||||
if !config.backend_options.is_object() {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"database_max_connections_zero",
|
||||
"database connection limits must be greater than zero",
|
||||
"database_backend_options_invalid",
|
||||
"database.backend_options must be a JSON object",
|
||||
));
|
||||
}
|
||||
if config.postgres.connect_timeout_ms == 0 || config.sqlite.busy_timeout_ms == 0 {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"database_timeout_zero",
|
||||
"database timeouts must be greater than zero",
|
||||
));
|
||||
}
|
||||
return require_non_empty(&config.sqlite.path, "database.sqlite.path");
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn validate_solana(config: &SolanaConfig) -> ks_core::Result<()> {
|
||||
fn validate_solana(config: &crate::SolanaConfig) -> ks_core::Result<()> {
|
||||
if config.http_endpoints.is_empty() {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
"http_endpoints_empty",
|
||||
@@ -522,7 +478,7 @@ fn validate_solana(config: &SolanaConfig) -> ks_core::Result<()> {
|
||||
endpoint.name.clone(),
|
||||
));
|
||||
}
|
||||
match validate_http_endpoint(endpoint) {
|
||||
match crate::validate_http_endpoint(endpoint) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -535,15 +491,15 @@ fn validate_solana(config: &SolanaConfig) -> ks_core::Result<()> {
|
||||
endpoint.name.clone(),
|
||||
));
|
||||
}
|
||||
match validate_ws_endpoint(endpoint) {
|
||||
match crate::validate_ws_endpoint(endpoint) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return validate_listeners(&config.listeners);
|
||||
return crate::validate_listeners(&config.listeners);
|
||||
}
|
||||
|
||||
pub(crate) fn validate_http_endpoint(config: &HttpEndpointConfig) -> ks_core::Result<()> {
|
||||
pub(crate) fn validate_http_endpoint(config: &crate::HttpEndpointConfig) -> ks_core::Result<()> {
|
||||
match validate_endpoint_common(&config.name, &config.provider, &config.cluster, &config.roles) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -574,7 +530,7 @@ pub(crate) fn validate_http_endpoint(config: &HttpEndpointConfig) -> ks_core::Re
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
pub(crate) fn validate_ws_endpoint(config: &WsEndpointConfig) -> ks_core::Result<()> {
|
||||
pub(crate) fn validate_ws_endpoint(config: &crate::WsEndpointConfig) -> ks_core::Result<()> {
|
||||
match validate_endpoint_common(&config.name, &config.provider, &config.cluster, &config.roles) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -603,17 +559,17 @@ fn validate_endpoint_common(
|
||||
name: &str,
|
||||
provider: &str,
|
||||
cluster: &str,
|
||||
roles: &[EndpointRoleConfig],
|
||||
roles: &[crate::EndpointRoleConfig],
|
||||
) -> ks_core::Result<()> {
|
||||
match require_non_empty(name, "endpoint.name") {
|
||||
match crate::require_non_empty(name, "endpoint.name") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match require_non_empty(provider, "endpoint.provider") {
|
||||
match crate::require_non_empty(provider, "endpoint.provider") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match require_non_empty(cluster, "endpoint.cluster") {
|
||||
match crate::require_non_empty(cluster, "endpoint.cluster") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -639,8 +595,8 @@ fn validate_endpoint_common(
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn validate_endpoint_role(config: &EndpointRoleConfig) -> ks_core::Result<()> {
|
||||
match require_non_empty(&config.role, "endpoint.roles.role") {
|
||||
fn validate_endpoint_role(config: &crate::EndpointRoleConfig) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&config.role, "endpoint.roles.role") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -651,7 +607,7 @@ fn validate_endpoint_role(config: &EndpointRoleConfig) -> ks_core::Result<()> {
|
||||
));
|
||||
}
|
||||
for request_kind in &config.request_kinds {
|
||||
match require_non_empty(request_kind, "endpoint.roles.request_kinds") {
|
||||
match crate::require_non_empty(request_kind, "endpoint.roles.request_kinds") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -669,8 +625,11 @@ fn validate_endpoint_role(config: &EndpointRoleConfig) -> ks_core::Result<()> {
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
pub(crate) fn validate_listeners(config: &ListenerConfig) -> ks_core::Result<()> {
|
||||
match require_non_empty(&config.default_commitment, "solana.listeners.default_commitment") {
|
||||
pub(crate) fn validate_listeners(config: &crate::ListenerConfig) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(
|
||||
&config.default_commitment,
|
||||
"solana.listeners.default_commitment",
|
||||
) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -695,62 +654,65 @@ pub(crate) fn validate_listeners(config: &ListenerConfig) -> ks_core::Result<()>
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn validate_log_listener(config: &LogListenerConfig) -> ks_core::Result<()> {
|
||||
match require_non_empty(&config.name, "log_listener.name") {
|
||||
fn validate_log_listener(config: &crate::LogListenerConfig) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&config.name, "log_listener.name") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match require_non_empty(&config.endpoint_role, "log_listener.endpoint_role") {
|
||||
match crate::require_non_empty(&config.endpoint_role, "log_listener.endpoint_role") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
return require_non_empty(&config.program_id, "log_listener.program_id");
|
||||
return crate::require_non_empty(&config.program_id, "log_listener.program_id");
|
||||
}
|
||||
|
||||
fn validate_program_listener(config: &ProgramListenerConfig) -> ks_core::Result<()> {
|
||||
match require_non_empty(&config.name, "program_listener.name") {
|
||||
fn validate_program_listener(config: &crate::ProgramListenerConfig) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&config.name, "program_listener.name") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match require_non_empty(&config.endpoint_role, "program_listener.endpoint_role") {
|
||||
match crate::require_non_empty(&config.endpoint_role, "program_listener.endpoint_role") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
return require_non_empty(&config.program_id, "program_listener.program_id");
|
||||
return crate::require_non_empty(&config.program_id, "program_listener.program_id");
|
||||
}
|
||||
|
||||
fn validate_account_listener(config: &AccountListenerConfig) -> ks_core::Result<()> {
|
||||
match require_non_empty(&config.name, "account_listener.name") {
|
||||
fn validate_account_listener(config: &crate::AccountListenerConfig) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&config.name, "account_listener.name") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match require_non_empty(&config.endpoint_role, "account_listener.endpoint_role") {
|
||||
match crate::require_non_empty(&config.endpoint_role, "account_listener.endpoint_role") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
return require_non_empty(&config.account_pubkey, "account_listener.account_pubkey");
|
||||
return crate::require_non_empty(&config.account_pubkey, "account_listener.account_pubkey");
|
||||
}
|
||||
|
||||
pub(crate) fn validate_wallet(config: &WalletConfig) -> ks_core::Result<()> {
|
||||
match require_non_empty(&config.wallet_dir, "wallet.wallet_dir") {
|
||||
pub(crate) fn validate_wallet(config: &crate::WalletConfig) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&config.wallet_dir, "wallet.wallet_dir") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match require_non_empty(&config.temporary_wallet_dir, "wallet.temporary_wallet_dir") {
|
||||
match crate::require_non_empty(&config.temporary_wallet_dir, "wallet.temporary_wallet_dir") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
match require_non_empty(&config.cluster, "wallet.cluster") {
|
||||
match crate::require_non_empty(&config.cluster, "wallet.cluster") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
if let std::option::Option::Some(alias) = config.wallet_alias.as_deref() {
|
||||
match validate_wallet_alias(alias, "wallet.wallet_alias") {
|
||||
match crate::validate_wallet_alias(alias, "wallet.wallet_alias") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
match validate_wallet_alias(&config.temporary_wallet_alias, "wallet.temporary_wallet_alias") {
|
||||
match crate::validate_wallet_alias(
|
||||
&config.temporary_wallet_alias,
|
||||
"wallet.temporary_wallet_alias",
|
||||
) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
@@ -787,7 +749,7 @@ pub(crate) fn validate_wallet_alias(value: &str, field: &str) -> ks_core::Result
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
pub(crate) fn validate_execution(config: &ExecutionConfig) -> ks_core::Result<()> {
|
||||
pub(crate) fn validate_execution(config: &crate::ExecutionConfig) -> ks_core::Result<()> {
|
||||
let configured_spend = config
|
||||
.localnet_max_spend_lamports
|
||||
.saturating_add(config.devnet_max_spend_lamports)
|
||||
@@ -833,8 +795,8 @@ pub(crate) fn validate_execution(config: &ExecutionConfig) -> ks_core::Result<()
|
||||
}
|
||||
|
||||
fn validate_wallet_execution_pair(
|
||||
wallet: &WalletConfig,
|
||||
execution: &ExecutionConfig,
|
||||
wallet: &crate::WalletConfig,
|
||||
execution: &crate::ExecutionConfig,
|
||||
) -> ks_core::Result<()> {
|
||||
for (enabled, limit, cluster) in [
|
||||
(
|
||||
@@ -909,25 +871,25 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn schema_text_is_valid_json() {
|
||||
let result = super::config_json_schema_value();
|
||||
let result = crate::config_json_schema_value();
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_and_example_app_configs_validate_against_schema() {
|
||||
assert!(super::validate_config_json_schema(DEFAULT_CONFIG).is_ok());
|
||||
assert!(super::validate_config_json_schema(EXAMPLE_CONFIG).is_ok());
|
||||
assert!(crate::validate_config_json_schema(DEFAULT_CONFIG).is_ok());
|
||||
assert!(crate::validate_config_json_schema(EXAMPLE_CONFIG).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_app_config_parses_and_resolves_active_profile() {
|
||||
let config_result = super::parse_config_json(DEFAULT_CONFIG);
|
||||
let config_result = crate::parse_config_json(DEFAULT_CONFIG);
|
||||
assert!(config_result.is_ok());
|
||||
let config = match config_result {
|
||||
std::result::Result::Ok(config) => config,
|
||||
std::result::Result::Err(error) => panic!("default app config must parse: {error}"),
|
||||
};
|
||||
let active_result = super::active_profile(&config);
|
||||
let active_result = crate::active_profile(&config);
|
||||
assert!(active_result.is_ok());
|
||||
//let active =
|
||||
match active_result {
|
||||
@@ -942,33 +904,62 @@ mod tests {
|
||||
let mut value = parse_default_value();
|
||||
value["active_profile"] = serde_json::Value::String("missing_profile".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn default_app_config_separates_runtime_postgres_profiles() {
|
||||
fn default_app_config_separates_selected_store_backend_options() {
|
||||
let value = parse_default_value();
|
||||
assert_eq!(
|
||||
value["profiles"][0]["database"]["postgres"]["url"],
|
||||
value["profiles"][0]["database"]["backend_options"]["url"],
|
||||
serde_json::Value::String("${KS_SECRET_POSTGRES_DEVNET_URL}".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
value["profiles"][1]["database"]["postgres"]["url"],
|
||||
value["profiles"][1]["database"]["backend_options"]["url"],
|
||||
serde_json::Value::String("${KS_SECRET_POSTGRES_MAINNET_URL}".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
value["profiles"][2]["database"]["postgres"]["url"],
|
||||
value["profiles"][2]["database"]["backend_options"]["url"],
|
||||
serde_json::Value::String("${KS_SECRET_POSTGRES_MAINNET_URL}".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parser_keeps_unknown_store_backend_options_opaque() {
|
||||
let mut value = parse_default_value();
|
||||
value["profiles"][0]["database"]["backend"] =
|
||||
serde_json::Value::String("future-backend".to_string());
|
||||
value["profiles"][0]["database"]["backend_options"] =
|
||||
serde_json::json!({"secret": "STORE-SECRET-CANARY"});
|
||||
let raw_json = value_to_json(&value);
|
||||
let config = match crate::parse_config_json(&raw_json) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("opaque backend must parse: {error}"),
|
||||
};
|
||||
assert_eq!(config.profiles[0].database.backend, "future-backend");
|
||||
assert_eq!(
|
||||
config.profiles[0].database.backend_options["secret"],
|
||||
serde_json::Value::String("STORE-SECRET-CANARY".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schema_rejects_non_object_store_backend_options() {
|
||||
let mut value = parse_default_value();
|
||||
value["profiles"][0]["database"]["backend_options"] =
|
||||
serde_json::Value::String("invalid".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = crate::validate_config_json_schema(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn schema_rejects_profile_enabled_field() {
|
||||
let mut value = parse_default_value();
|
||||
value["profiles"][0]["enabled"] = serde_json::Value::Bool(true);
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::validate_config_json_schema(&raw_json);
|
||||
let result = crate::validate_config_json_schema(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -977,7 +968,7 @@ mod tests {
|
||||
let mut value = parse_default_value();
|
||||
value["profiles"][1]["name"] = serde_json::Value::String("local_devnet".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -987,7 +978,7 @@ mod tests {
|
||||
value["profiles"][0]["solana"]["http_endpoints"][0]["secret_env_field"] =
|
||||
serde_json::Value::String("KS_SECRET_HELIUS_API_KEY".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::validate_config_json_schema(&raw_json);
|
||||
let result = crate::validate_config_json_schema(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -997,7 +988,7 @@ mod tests {
|
||||
value["profiles"][0]["data"]["idls_directory"] =
|
||||
serde_json::Value::String("idls".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::validate_config_json_schema(&raw_json);
|
||||
let result = crate::validate_config_json_schema(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -1006,7 +997,7 @@ mod tests {
|
||||
let mut value = parse_default_value();
|
||||
value["profiles"][0]["solana"]["advanced_streams"] = serde_json::json!([]);
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::validate_config_json_schema(&raw_json);
|
||||
let result = crate::validate_config_json_schema(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -1016,7 +1007,7 @@ mod tests {
|
||||
value["profiles"][0]["solana"]["http_endpoints"][0]["url"] =
|
||||
serde_json::Value::String("wss://api.devnet.solana.com".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -1026,7 +1017,7 @@ mod tests {
|
||||
value["profiles"][0]["solana"]["ws_endpoints"][0]["url"] =
|
||||
serde_json::Value::String("https://api.devnet.solana.com".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -1036,7 +1027,7 @@ mod tests {
|
||||
value["profiles"][0]["wallet"]["wallet_alias"] =
|
||||
serde_json::Value::String("../wallet".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -1046,7 +1037,7 @@ mod tests {
|
||||
value["profiles"][0]["wallet"]["temporary_wallet_alias"] =
|
||||
serde_json::Value::String("../wallet".to_string());
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -1055,7 +1046,7 @@ mod tests {
|
||||
let mut value = parse_default_value();
|
||||
value["profiles"][0]["wallet"]["temporary_wallet_enabled"] = serde_json::Value::Bool(false);
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -1065,7 +1056,7 @@ mod tests {
|
||||
value["profiles"][0]["execution"]["devnet_max_spend_lamports"] =
|
||||
serde_json::Value::Number(serde_json::Number::from(0));
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
@@ -1081,7 +1072,7 @@ mod tests {
|
||||
value["profiles"][0]["execution"][field] =
|
||||
serde_json::Value::Number(serde_json::Number::from(0));
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
@@ -1092,7 +1083,7 @@ mod tests {
|
||||
value["profiles"][0]["solana"]["http_endpoints"][0]["roles"][0]["max_subscriptions"] =
|
||||
serde_json::Value::Number(serde_json::Number::from(1));
|
||||
let raw_json = value_to_json(&value);
|
||||
let result = super::parse_config_json(&raw_json);
|
||||
let result = crate::parse_config_json(&raw_json);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/store.rs
|
||||
// version: 2
|
||||
// version: 4
|
||||
|
||||
//! Independent store configuration document loading and validation.
|
||||
|
||||
@@ -11,7 +11,7 @@ pub struct StoreConfigDocument {
|
||||
/// Default store profile used when a composition does not override it.
|
||||
pub default_profile: std::string::String,
|
||||
/// Named store profiles.
|
||||
pub profiles: std::vec::Vec<StoreProfileConfig>,
|
||||
pub profiles: std::vec::Vec<crate::StoreProfileConfig>,
|
||||
}
|
||||
|
||||
/// Named store profile.
|
||||
@@ -42,7 +42,7 @@ pub fn store_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||
|
||||
/// Validates raw store JSON against the embedded schema.
|
||||
pub fn validate_store_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
let schema = match store_json_schema_value() {
|
||||
let schema = match crate::store_json_schema_value() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -74,12 +74,12 @@ pub fn validate_store_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
}
|
||||
|
||||
/// Parses and validates one store configuration document.
|
||||
pub fn parse_store_json(raw_json: &str) -> ks_core::Result<StoreConfigDocument> {
|
||||
match validate_store_json_schema(raw_json) {
|
||||
pub fn parse_store_json(raw_json: &str) -> ks_core::Result<crate::StoreConfigDocument> {
|
||||
match crate::validate_store_json_schema(raw_json) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let document = match serde_json::from_str::<StoreConfigDocument>(raw_json) {
|
||||
let document = match serde_json::from_str::<crate::StoreConfigDocument>(raw_json) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
@@ -88,7 +88,7 @@ pub fn parse_store_json(raw_json: &str) -> ks_core::Result<StoreConfigDocument>
|
||||
));
|
||||
},
|
||||
};
|
||||
return match validate_store_document(&document) {
|
||||
return match crate::validate_store_document(&document) {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(document),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -98,7 +98,7 @@ pub fn parse_store_json(raw_json: &str) -> ks_core::Result<StoreConfigDocument>
|
||||
pub fn read_store_json_file_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<StoreConfigDocument> {
|
||||
) -> ks_core::Result<crate::StoreConfigDocument> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -112,14 +112,14 @@ pub fn read_store_json_file_with_environment(
|
||||
));
|
||||
},
|
||||
};
|
||||
return parse_store_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
return crate::parse_store_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
}
|
||||
|
||||
/// Returns one named store profile.
|
||||
pub fn store_profile<'a>(
|
||||
document: &'a StoreConfigDocument,
|
||||
document: &'a crate::StoreConfigDocument,
|
||||
profile_name: &str,
|
||||
) -> ks_core::Result<&'a StoreProfileConfig> {
|
||||
) -> ks_core::Result<&'a crate::StoreProfileConfig> {
|
||||
for profile in &document.profiles {
|
||||
if profile.name == profile_name {
|
||||
return std::result::Result::Ok(profile);
|
||||
@@ -133,13 +133,13 @@ pub fn store_profile<'a>(
|
||||
|
||||
/// Returns the default store profile.
|
||||
pub fn default_store_profile(
|
||||
document: &StoreConfigDocument,
|
||||
) -> ks_core::Result<&StoreProfileConfig> {
|
||||
return store_profile(document, document.default_profile.as_str());
|
||||
document: &crate::StoreConfigDocument,
|
||||
) -> ks_core::Result<&crate::StoreProfileConfig> {
|
||||
return crate::store_profile(document, document.default_profile.as_str());
|
||||
}
|
||||
|
||||
/// Validates one typed store configuration document.
|
||||
pub fn validate_store_document(document: &StoreConfigDocument) -> ks_core::Result<()> {
|
||||
pub fn validate_store_document(document: &crate::StoreConfigDocument) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&document.default_profile, "store.default_profile") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -167,7 +167,7 @@ pub fn validate_store_document(document: &StoreConfigDocument) -> ks_core::Resul
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return match default_store_profile(document) {
|
||||
return match crate::default_store_profile(document) {
|
||||
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -180,7 +180,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn default_and_example_store_configs_validate() {
|
||||
assert!(super::parse_store_json(DEFAULT_STORE).is_ok());
|
||||
assert!(super::parse_store_json(EXAMPLE_STORE).is_ok());
|
||||
assert!(crate::parse_store_json(DEFAULT_STORE).is_ok());
|
||||
assert!(crate::parse_store_json(EXAMPLE_STORE).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/transport.rs
|
||||
// version: 3
|
||||
// version: 5
|
||||
|
||||
//! Independent Solana transport configuration document loading and validation.
|
||||
|
||||
@@ -12,9 +12,9 @@ pub struct TransportConfigDocument {
|
||||
/// Default transport profile used when a composition does not override it.
|
||||
pub default_profile: std::string::String,
|
||||
/// Named WebSocket endpoint defaults resolved before endpoint-specific overrides.
|
||||
pub ws_endpoint_defaults: std::vec::Vec<WsEndpointDefaultsConfig>,
|
||||
pub ws_endpoint_defaults: std::vec::Vec<crate::WsEndpointDefaultsConfig>,
|
||||
/// Named transport profiles available in this document.
|
||||
pub profiles: std::vec::Vec<TransportProfileSourceConfig>,
|
||||
pub profiles: std::vec::Vec<crate::TransportProfileSourceConfig>,
|
||||
}
|
||||
|
||||
/// Default values shared by one class of WebSocket endpoints.
|
||||
@@ -70,7 +70,7 @@ pub struct WsEndpointSourceConfig {
|
||||
pub url: std::string::String,
|
||||
/// Optional endpoint-specific overrides.
|
||||
#[serde(default)]
|
||||
pub overrides: WsEndpointOverridesConfig,
|
||||
pub overrides: crate::WsEndpointOverridesConfig,
|
||||
/// Role-specific limits for this URL.
|
||||
pub roles: std::vec::Vec<crate::EndpointRoleConfig>,
|
||||
}
|
||||
@@ -83,7 +83,7 @@ pub struct TransportProfileSourceConfig {
|
||||
/// HTTP JSON-RPC endpoints.
|
||||
pub http_endpoints: std::vec::Vec<crate::HttpEndpointConfig>,
|
||||
/// Standard Solana WebSocket endpoints before shared defaults are resolved.
|
||||
pub ws_endpoints: std::vec::Vec<WsEndpointSourceConfig>,
|
||||
pub ws_endpoints: std::vec::Vec<crate::WsEndpointSourceConfig>,
|
||||
}
|
||||
|
||||
/// Fully resolved transport profile consumed by transport clients.
|
||||
@@ -116,7 +116,7 @@ pub fn transport_json_schema_value() -> ks_core::Result<serde_json::Value> {
|
||||
|
||||
/// Validates raw transport JSON against the embedded transport schema.
|
||||
pub fn validate_transport_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
let schema = match transport_json_schema_value() {
|
||||
let schema = match crate::transport_json_schema_value() {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -148,12 +148,12 @@ pub fn validate_transport_json_schema(raw_json: &str) -> ks_core::Result<()> {
|
||||
}
|
||||
|
||||
/// Parses and validates one independent transport configuration document.
|
||||
pub fn parse_transport_json(raw_json: &str) -> ks_core::Result<TransportConfigDocument> {
|
||||
match validate_transport_json_schema(raw_json) {
|
||||
pub fn parse_transport_json(raw_json: &str) -> ks_core::Result<crate::TransportConfigDocument> {
|
||||
match crate::validate_transport_json_schema(raw_json) {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
let document = match serde_json::from_str::<TransportConfigDocument>(raw_json) {
|
||||
let document = match serde_json::from_str::<crate::TransportConfigDocument>(raw_json) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
@@ -162,7 +162,7 @@ pub fn parse_transport_json(raw_json: &str) -> ks_core::Result<TransportConfigDo
|
||||
));
|
||||
},
|
||||
};
|
||||
return match validate_transport_document(&document) {
|
||||
return match crate::validate_transport_document(&document) {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(document),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -172,7 +172,7 @@ pub fn parse_transport_json(raw_json: &str) -> ks_core::Result<TransportConfigDo
|
||||
pub fn read_transport_json_file_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<TransportConfigDocument> {
|
||||
) -> ks_core::Result<crate::TransportConfigDocument> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -186,14 +186,14 @@ pub fn read_transport_json_file_with_environment(
|
||||
));
|
||||
},
|
||||
};
|
||||
return parse_transport_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
return crate::parse_transport_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
}
|
||||
|
||||
/// Resolves one named transport profile including WebSocket defaults and overrides.
|
||||
pub fn transport_profile(
|
||||
document: &TransportConfigDocument,
|
||||
document: &crate::TransportConfigDocument,
|
||||
profile_name: &str,
|
||||
) -> ks_core::Result<TransportProfileConfig> {
|
||||
) -> ks_core::Result<crate::TransportProfileConfig> {
|
||||
let source = match source_transport_profile(document, profile_name) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -259,7 +259,7 @@ pub fn transport_profile(
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(TransportProfileConfig {
|
||||
return std::result::Result::Ok(crate::TransportProfileConfig {
|
||||
name: source.name.clone(),
|
||||
http_endpoints: source.http_endpoints.clone(),
|
||||
ws_endpoints,
|
||||
@@ -268,15 +268,15 @@ pub fn transport_profile(
|
||||
|
||||
/// Returns the default resolved transport profile.
|
||||
pub fn default_transport_profile(
|
||||
document: &TransportConfigDocument,
|
||||
) -> ks_core::Result<TransportProfileConfig> {
|
||||
return transport_profile(document, document.default_profile.as_str());
|
||||
document: &crate::TransportConfigDocument,
|
||||
) -> ks_core::Result<crate::TransportProfileConfig> {
|
||||
return crate::transport_profile(document, document.default_profile.as_str());
|
||||
}
|
||||
|
||||
fn source_transport_profile<'a>(
|
||||
document: &'a TransportConfigDocument,
|
||||
document: &'a crate::TransportConfigDocument,
|
||||
profile_name: &str,
|
||||
) -> ks_core::Result<&'a TransportProfileSourceConfig> {
|
||||
) -> ks_core::Result<&'a crate::TransportProfileSourceConfig> {
|
||||
for profile in &document.profiles {
|
||||
if profile.name == profile_name {
|
||||
return std::result::Result::Ok(profile);
|
||||
@@ -289,9 +289,9 @@ fn source_transport_profile<'a>(
|
||||
}
|
||||
|
||||
fn ws_endpoint_defaults<'a>(
|
||||
document: &'a TransportConfigDocument,
|
||||
document: &'a crate::TransportConfigDocument,
|
||||
defaults_name: &str,
|
||||
) -> ks_core::Result<&'a WsEndpointDefaultsConfig> {
|
||||
) -> ks_core::Result<&'a crate::WsEndpointDefaultsConfig> {
|
||||
for defaults in &document.ws_endpoint_defaults {
|
||||
if defaults.name == defaults_name {
|
||||
return std::result::Result::Ok(defaults);
|
||||
@@ -304,7 +304,9 @@ fn ws_endpoint_defaults<'a>(
|
||||
}
|
||||
|
||||
/// Validates a typed transport configuration document.
|
||||
pub fn validate_transport_document(document: &TransportConfigDocument) -> ks_core::Result<()> {
|
||||
pub fn validate_transport_document(
|
||||
document: &crate::TransportConfigDocument,
|
||||
) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&document.default_profile, "transport.default_profile") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -379,12 +381,12 @@ pub fn validate_transport_document(document: &TransportConfigDocument) -> ks_cor
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
match transport_profile(document, profile.name.as_str()) {
|
||||
match crate::transport_profile(document, profile.name.as_str()) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return match default_transport_profile(document) {
|
||||
return match crate::default_transport_profile(document) {
|
||||
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -398,17 +400,17 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn default_and_example_transport_configs_validate() {
|
||||
assert!(super::parse_transport_json(DEFAULT_TRANSPORT).is_ok());
|
||||
assert!(super::parse_transport_json(EXAMPLE_TRANSPORT).is_ok());
|
||||
assert!(crate::parse_transport_json(DEFAULT_TRANSPORT).is_ok());
|
||||
assert!(crate::parse_transport_json(EXAMPLE_TRANSPORT).is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn websocket_defaults_are_resolved_before_endpoint_overrides() {
|
||||
let document = match super::parse_transport_json(DEFAULT_TRANSPORT) {
|
||||
let document = match crate::parse_transport_json(DEFAULT_TRANSPORT) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("default transport must parse: {error}"),
|
||||
};
|
||||
let profile = match super::transport_profile(&document, "mainnet_research") {
|
||||
let profile = match crate::transport_profile(&document, "mainnet_research") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
panic!("mainnet research transport must resolve: {error}")
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: ks-config/src/wallet.rs
|
||||
// version: 6
|
||||
// version: 8
|
||||
|
||||
//! Independent wallet configuration document loading and validation.
|
||||
|
||||
@@ -13,7 +13,7 @@ pub struct WalletConfigDocument {
|
||||
/// Default wallet profile used when a composition does not override it.
|
||||
pub default_profile: std::string::String,
|
||||
/// Named wallet profiles.
|
||||
pub profiles: std::vec::Vec<WalletProfileConfig>,
|
||||
pub profiles: std::vec::Vec<crate::WalletProfileConfig>,
|
||||
}
|
||||
|
||||
/// Named wallet profile whose directory is relative to the global wallet root unless absolute.
|
||||
@@ -41,7 +41,7 @@ pub fn wallet_json_schema_text() -> &'static str {
|
||||
}
|
||||
|
||||
/// Parses and validates one wallet configuration document.
|
||||
pub fn parse_wallet_json(raw_json: &str) -> ks_core::Result<WalletConfigDocument> {
|
||||
pub fn parse_wallet_json(raw_json: &str) -> ks_core::Result<crate::WalletConfigDocument> {
|
||||
let schema = match serde_json::from_str::<serde_json::Value>(WALLET_JSON_SCHEMA) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => {
|
||||
@@ -75,7 +75,7 @@ pub fn parse_wallet_json(raw_json: &str) -> ks_core::Result<WalletConfigDocument
|
||||
"wallet configuration does not satisfy its schema",
|
||||
));
|
||||
}
|
||||
let document = match serde_json::from_str::<WalletConfigDocument>(raw_json) {
|
||||
let document = match serde_json::from_str::<crate::WalletConfigDocument>(raw_json) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => {
|
||||
return std::result::Result::Err(ks_core::Error::new(
|
||||
@@ -84,7 +84,7 @@ pub fn parse_wallet_json(raw_json: &str) -> ks_core::Result<WalletConfigDocument
|
||||
));
|
||||
},
|
||||
};
|
||||
return match validate_wallet_document(&document) {
|
||||
return match crate::validate_wallet_document(&document) {
|
||||
std::result::Result::Ok(()) => std::result::Result::Ok(document),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -94,7 +94,7 @@ pub fn parse_wallet_json(raw_json: &str) -> ks_core::Result<WalletConfigDocument
|
||||
pub fn read_wallet_json_file_with_environment(
|
||||
path: &std::path::Path,
|
||||
workspace_root: &std::path::Path,
|
||||
) -> ks_core::Result<WalletConfigDocument> {
|
||||
) -> ks_core::Result<crate::WalletConfigDocument> {
|
||||
match crate::load_workspace_environment(workspace_root) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -108,14 +108,14 @@ pub fn read_wallet_json_file_with_environment(
|
||||
));
|
||||
},
|
||||
};
|
||||
return parse_wallet_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
return crate::parse_wallet_json(&crate::resolve_environment_placeholders(&raw_json));
|
||||
}
|
||||
|
||||
/// Returns one named wallet source profile.
|
||||
pub fn wallet_profile<'a>(
|
||||
document: &'a WalletConfigDocument,
|
||||
document: &'a crate::WalletConfigDocument,
|
||||
profile_name: &str,
|
||||
) -> ks_core::Result<&'a WalletProfileConfig> {
|
||||
) -> ks_core::Result<&'a crate::WalletProfileConfig> {
|
||||
for profile in &document.profiles {
|
||||
if profile.name == profile_name {
|
||||
return std::result::Result::Ok(profile);
|
||||
@@ -129,17 +129,17 @@ pub fn wallet_profile<'a>(
|
||||
|
||||
/// Returns the default wallet source profile.
|
||||
pub fn default_wallet_profile(
|
||||
document: &WalletConfigDocument,
|
||||
) -> ks_core::Result<&WalletProfileConfig> {
|
||||
return wallet_profile(document, document.default_profile.as_str());
|
||||
document: &crate::WalletConfigDocument,
|
||||
) -> ks_core::Result<&crate::WalletProfileConfig> {
|
||||
return crate::wallet_profile(document, document.default_profile.as_str());
|
||||
}
|
||||
|
||||
/// Resolves one source wallet profile into the runtime wallet contract.
|
||||
pub fn resolved_wallet_profile(
|
||||
document: &WalletConfigDocument,
|
||||
document: &crate::WalletConfigDocument,
|
||||
profile_name: &str,
|
||||
) -> ks_core::Result<crate::WalletConfig> {
|
||||
let profile = match wallet_profile(document, profile_name) {
|
||||
let profile = match crate::wallet_profile(document, profile_name) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
@@ -166,7 +166,7 @@ pub fn resolved_wallet_profile(
|
||||
}
|
||||
|
||||
/// Validates one typed wallet configuration document.
|
||||
pub fn validate_wallet_document(document: &WalletConfigDocument) -> ks_core::Result<()> {
|
||||
pub fn validate_wallet_document(document: &crate::WalletConfigDocument) -> ks_core::Result<()> {
|
||||
match crate::require_non_empty(&document.wallets_directory, "wallet.wallets_directory") {
|
||||
std::result::Result::Ok(()) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
@@ -203,12 +203,12 @@ pub fn validate_wallet_document(document: &WalletConfigDocument) -> ks_core::Res
|
||||
profile.name.clone(),
|
||||
));
|
||||
}
|
||||
match resolved_wallet_profile(document, profile.name.as_str()) {
|
||||
match crate::resolved_wallet_profile(document, profile.name.as_str()) {
|
||||
std::result::Result::Ok(_) => (),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return match default_wallet_profile(document) {
|
||||
return match crate::default_wallet_profile(document) {
|
||||
std::result::Result::Ok(_) => std::result::Result::Ok(()),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
@@ -221,9 +221,9 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn default_and_example_wallet_configs_validate() {
|
||||
let default = super::parse_wallet_json(DEFAULT_WALLET);
|
||||
let default = crate::parse_wallet_json(DEFAULT_WALLET);
|
||||
assert!(default.is_ok());
|
||||
let example = super::parse_wallet_json(EXAMPLE_WALLET);
|
||||
let example = crate::parse_wallet_json(EXAMPLE_WALLET);
|
||||
assert!(example.is_ok());
|
||||
let example = match example {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -238,11 +238,11 @@ mod tests {
|
||||
#[test]
|
||||
fn resolved_profile_separates_persistent_and_temporary_wallet_directories() {
|
||||
let resolved_raw = crate::resolve_environment_placeholders(DEFAULT_WALLET);
|
||||
let document = match super::parse_wallet_json(resolved_raw.as_str()) {
|
||||
let document = match crate::parse_wallet_json(resolved_raw.as_str()) {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("resolved wallet config failed: {error}"),
|
||||
};
|
||||
let resolved = match super::resolved_wallet_profile(&document, "local_devnet") {
|
||||
let resolved = match crate::resolved_wallet_profile(&document, "local_devnet") {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet profile resolution failed: {error}"),
|
||||
};
|
||||
@@ -269,7 +269,7 @@ mod tests {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet JSON encode failed: {error}"),
|
||||
};
|
||||
let parsed = super::parse_wallet_json(raw.as_str());
|
||||
let parsed = crate::parse_wallet_json(raw.as_str());
|
||||
assert!(parsed.is_ok());
|
||||
let parsed = match parsed {
|
||||
std::result::Result::Ok(value) => value,
|
||||
@@ -289,6 +289,6 @@ mod tests {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => panic!("wallet JSON encode failed: {error}"),
|
||||
};
|
||||
assert!(super::parse_wallet_json(raw.as_str()).is_err());
|
||||
assert!(crate::parse_wallet_json(raw.as_str()).is_err());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user