Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8c65096f4c | |||
| cb142b1cb8 | |||
| 08c8046262 | |||
| c0b131bf6f | |||
| 5bf9651038 | |||
| e91bf36e9e | |||
| 1125ade4c1 | |||
| 518cc9257f | |||
| 46afe10423 | |||
| 25edcc231e | |||
| 6094d33120 | |||
| dcd23abb92 | |||
| 36b98e0abc | |||
| 3c069347b7 | |||
| a0a5595186 | |||
| 6c1172efc3 | |||
| d5536b0778 | |||
| c6794af05b | |||
| bcda6db11f | |||
| 51b5114ae2 | |||
| c97a8739ab | |||
| c2ccef3849 | |||
| 58134c81d1 | |||
| 5b3967327a | |||
| 804b92b957 | |||
| e6772c109d | |||
| 7e84522787 | |||
| 37461ec248 | |||
| 4252a21140 | |||
| 7a889dd466 | |||
| d3cc7c93f0 | |||
| 561b6678ed | |||
| e500024c3a | |||
| 033800f897 | |||
| cf8d79567d | |||
| cb19742cf7 | |||
| c03a3eaf5d | |||
| f7acac3fc8 | |||
| 039b93cfb3 | |||
| c9a0f7fd37 | |||
| 704110d509 | |||
| fb32ed03f8 | |||
| 4439589f0d | |||
| 1e1bc0d421 | |||
| bd26d4f636 | |||
| 2a7f3e8f40 | |||
| 1d37f04d63 | |||
| 1ccf11bcc1 | |||
| d06284fca2 |
5
.pydevproject
Normal file
5
.pydevproject
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<?eclipse-pydev version="1.0"?><pydev_project>
|
||||||
|
<pydev_property name="org.python.pydev.PYTHON_PROJECT_INTERPRETER">Default</pydev_property>
|
||||||
|
<pydev_property name="org.python.pydev.PYTHON_PROJECT_VERSION">python interpreter</pydev_property>
|
||||||
|
</pydev_project>
|
||||||
16
CHANGELOG.md
16
CHANGELOG.md
@@ -1,10 +1,24 @@
|
|||||||
<!-- file: CHANGELOG.md -->
|
<!-- file: CHANGELOG.md -->
|
||||||
<!-- version: 6 -->
|
<!-- version: 9 -->
|
||||||
|
|
||||||
# Changelog KSP
|
# Changelog KSP
|
||||||
|
|
||||||
|
## 0.2.5 — Wallet foundation — 2026-08-20
|
||||||
|
|
||||||
|
`0.2.5` publie `ksp-wallet-lib` et le format natif interopérable `.kspwallet` V1. La release stabilise les capacités cryptographiquement indépendantes VIEW/OWNER, le masquage de la Pubkey/alias/notes à l’état verrouillé, Argon2id v19 avec paramètres sérialisés et profil de création `65 536 KiB / 3 / 1`, XChaCha20-Poly1305, une autorité Ed25519 OWNER distincte de la keypair Solana, les compartiments `owner_control`/`metadata`/`secret`, la création et l’ouverture mémoire/fichier, la persistence no-clobber, la signature Solana OWNER sans getter secret, l’administration alias/notes, les rotations OWNER/VIEW, la révocation forte VIEW avec rekey metadata, la détection de handles stale et les adapters Solana CLI JSON/Base58 pour inspection/import/export. Le wire V1 est documenté indépendamment de Rust dans `docs/formats/KSPWALLET_V1.md` et couvert par des vecteurs publics ainsi que des canaris adversariaux et une reproduction externe. La release conserve Wallet indépendant de Config/Transport/ExecutionPolicy/Store/Tauri et expose la Pubkey via `ksp-core-lib` tandis que `solana-keypair` reste encapsulée dans Wallet.
|
||||||
|
|
||||||
|
La clôture technique `pre.010-fix.001` à `fix.003` met la dépendance directe KSP à `ed25519-dalek 3.0.0`, accepte la génération `2.2.0` transitive encore imposée par `solana-keypair 3.1.2`, normalise les sources Rust du workspace et introduit `python3 scripts/audit_rust_workspace_rules.py` comme gate structurel complémentaire à rustfmt/Clippy. Le checkpoint final `pre.010-fix.003` est validé avec audit Python clean, `cargo check`, Clippy, tests Wallet et workspace complet verts. Le prompt `prompts/011-V0_2_6_START_PROMPT.md` ouvre ensuite `0.2.6 — Wallet Desk` par audit/sizing avant implémentation.
|
||||||
|
|
||||||
|
## 0.2.4 — HTTP Blocks + Economics + compliance HTTP finale — 2026-08-18
|
||||||
|
|
||||||
|
`0.2.4` achève la surface HTTP Solana standard de `ksp-onchain-transport-lib` : les 10 wrappers Blocks et 5 wrappers Economics portent la couverture typée à **52/52 méthodes HTTP courantes**, tandis que les **14/14 méthodes historiques Deprecated/runtime Removed** restent conservées pour compliance. La release stabilise notamment `getBlock` moderne avec sa forme bare encoding legacy dépréciée, les quatre variantes `transactionDetails`, les versions transaction numériques génériques, `numRewardPartitions` (SIMD-0118), `commissionBps` (SIMD-0291), les overloads de ranges/performance, `getBlockProduction`, les valeurs Economics fournies par le runtime et `getInflationReward` positionnel sans plafond d'adresses inventé. Le réaudit final confirme l'égalité exacte entre l'inventaire HTTP officiel courant et le registre KSP, applique `KSP-TRANSPORT-007` aux **52/52 wrappers**, et ne matérialise aucune extension spéculative issue de la watchlist SIMD. Les canaries de frontières, le workspace complet et les deux smokes Devnet — Transport pur couvrant Accounts/Tokens/Cluster/Transactions/Blocks/Economics et composition Config -> Transport transitoire — sont validés avant publication. Le prompt `prompts/010-V0_2_5_START_PROMPT.md`, enrichi par `pre.009-fix.001`, ouvre `0.2.5 — Wallet foundation` avec threat model, interopérabilité `.kspwallet` et capacités indépendantes VIEW/OWNER.
|
||||||
|
|
||||||
Ce changelog résume uniquement les releases KSP considérées comme stables, dans l'ordre chronologique décroissant. Les détails de chaque livraison restent dans `deltas/`.
|
Ce changelog résume uniquement les releases KSP considérées comme stables, dans l'ordre chronologique décroissant. Les détails de chaque livraison restent dans `deltas/`.
|
||||||
|
|
||||||
|
## 0.2.3 — HTTP Transactions — 2026-08-18
|
||||||
|
|
||||||
|
`0.2.3` complète la famille HTTP Transactions de `ksp-onchain-transport-lib` avec 11 wrappers typés, portant la surface HTTP typée courante à 37 méthodes : 4 foundation, 22 Accounts/Tokens/Cluster et 11 Transactions. La release stabilise la classification exacte `8 Read / RetrySafe`, `2 WriteSubmission / NeverAfterDispatch` (`requestAirdrop`, `sendTransaction`) et `1 Simulation / RetrySafe` (`simulateTransaction`), avec preuves end-to-end qu'une write submission n'est jamais resoumise après dispatch ambigu mais peut retenter lorsqu'un échec est prouvé `NotDispatched`. `getTransaction` couvre la config moderne complète et la forme bare encoding legacy dépréciée ; les variantes de transaction, metadata, version et `transactionIndex` restent préservées losslessly. `simulateTransaction` couvre sa config complète, ses invariants déterministes et son résultat riche sans introduire de client RPC Solana haut niveau ni de décodage transactionnel dans Transport. La release formalise `KSP-TRANSPORT-007` et réaudite rétroactivement les 37 wrappers courants sans remédiation fonctionnelle. Les canaries conservent 52 méthodes HTTP courantes, 14 historiques Deprecated/Removed et 15 Blocks/Economics réservées à `0.2.4`. Les graphes Cargo et les deux smokes Devnet — Transport pur read-only et composition Config -> Transport transitoire — ont été validés avant publication. Le prompt `prompts/009-V0_2_4_START_PROMPT.md` ouvre `0.2.4 — HTTP Blocks + Economics + compliance HTTP finale`.
|
||||||
|
|
||||||
## 0.2.2 — HTTP Accounts + Tokens + Cluster — 2026-08-18
|
## 0.2.2 — HTTP Accounts + Tokens + Cluster — 2026-08-18
|
||||||
|
|
||||||
`0.2.2` complète la surface HTTP typée de `ksp-onchain-transport-lib` avec 22 wrappers supplémentaires : 5 Accounts, 5 Tokens et 12 Cluster, portant la couverture typée totale à 26 méthodes avec les quatre canaris foundation de `0.2.1`. La release stabilise les DTOs/configs wire communs, préserve les formes Account encodées/`jsonParsed`, les `null` et champs optionnels, les selectors Token exclusifs, les contextes RPC, les structures Cluster/leader/vote et les limites locales auditées (`getMultipleAccounts <= 100`, `getProgramAccounts <= 4` filtres, `memcmp` raw <= 128 octets, `getSlotLeaders` entre 1 et 5000). Les 22 descriptors restent `Read / RetrySafe` et passent exclusivement par le flux central `descriptor -> execute_standard_rpc -> pool/admission -> executor HTTP`. La release n'ajoute aucune dépendance externe, ne crée aucun client HTTP parallèle et conserve le firewall Transport -> Config/Store/Program. Elle ajoute un smoke Devnet pur à Transport avec settings programmatiques couvrant Accounts/Tokens/Cluster, conserve séparément le smoke historique Config -> Transport comme exception transitoire, et valide les deux explicitement avant publication. Les canaries figent toujours 52 méthodes HTTP courantes, 14 historiques Deprecated/Removed et la partition typed `4 / 22 / 11 / 15`. Le prompt `prompts/008-V0_2_3_START_PROMPT.md` ouvre `0.2.3 — HTTP Transactions`.
|
`0.2.2` complète la surface HTTP typée de `ksp-onchain-transport-lib` avec 22 wrappers supplémentaires : 5 Accounts, 5 Tokens et 12 Cluster, portant la couverture typée totale à 26 méthodes avec les quatre canaris foundation de `0.2.1`. La release stabilise les DTOs/configs wire communs, préserve les formes Account encodées/`jsonParsed`, les `null` et champs optionnels, les selectors Token exclusifs, les contextes RPC, les structures Cluster/leader/vote et les limites locales auditées (`getMultipleAccounts <= 100`, `getProgramAccounts <= 4` filtres, `memcmp` raw <= 128 octets, `getSlotLeaders` entre 1 et 5000). Les 22 descriptors restent `Read / RetrySafe` et passent exclusivement par le flux central `descriptor -> execute_standard_rpc -> pool/admission -> executor HTTP`. La release n'ajoute aucune dépendance externe, ne crée aucun client HTTP parallèle et conserve le firewall Transport -> Config/Store/Program. Elle ajoute un smoke Devnet pur à Transport avec settings programmatiques couvrant Accounts/Tokens/Cluster, conserve séparément le smoke historique Config -> Transport comme exception transitoire, et valide les deux explicitement avant publication. Les canaries figent toujours 52 méthodes HTTP courantes, 14 historiques Deprecated/Removed et la partition typed `4 / 22 / 11 / 15`. Le prompt `prompts/008-V0_2_3_START_PROMPT.md` ouvre `0.2.3 — HTTP Transactions`.
|
||||||
|
|||||||
14
Cargo.toml
14
Cargo.toml
@@ -1,12 +1,12 @@
|
|||||||
# file: Cargo.toml
|
# file: Cargo.toml
|
||||||
# version: 119
|
# version: 163
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "3"
|
resolver = "3"
|
||||||
members = ["crates/ksp-app-config-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib"]
|
members = ["crates/ksp-app-config-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-logging-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-wallet-lib"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.2.2"
|
version = "0.2.5"
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project"
|
||||||
@@ -14,21 +14,29 @@ authors = ["SinuS von SifriduS <sinus@sasedev.net>"]
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
|
argon2 = { version = "^0.5", default-features = false }
|
||||||
|
chacha20poly1305 = { version = "^0.11", default-features = false }
|
||||||
|
ed25519-dalek = { version = "^3.0", default-features = false }
|
||||||
|
getrandom = { version = "^0.4", default-features = false }
|
||||||
|
base64 = { version = "^0.23" }
|
||||||
fs2 = { version = "^0.4" }
|
fs2 = { version = "^0.4" }
|
||||||
serde = { version = "^1.0" }
|
serde = { version = "^1.0" }
|
||||||
serde_json = { version = "^1.0" }
|
serde_json = { version = "^1.0" }
|
||||||
jsonschema = { version = "^0.49", default-features = false }
|
jsonschema = { version = "^0.49", default-features = false }
|
||||||
reqwest = { version = "^0.13", default-features = false }
|
reqwest = { version = "^0.13", default-features = false }
|
||||||
|
solana-keypair = { version = "^3.1", default-features = false }
|
||||||
solana-pubkey = { version = "^4.3", default-features = false }
|
solana-pubkey = { version = "^4.3", default-features = false }
|
||||||
tracing = { version = "^0.1", default-features = false }
|
tracing = { version = "^0.1", default-features = false }
|
||||||
tracing-subscriber = { version = "^0.3", default-features = false }
|
tracing-subscriber = { version = "^0.3", default-features = false }
|
||||||
tracing-appender = { version = "^0.2", default-features = false }
|
tracing-appender = { version = "^0.2", default-features = false }
|
||||||
tokio = { version = "^1.53", default-features = false }
|
tokio = { version = "^1.53", default-features = false }
|
||||||
|
tempfile = { version = "^3.27" }
|
||||||
chrono = { version = "^0.4", default-features = false }
|
chrono = { version = "^0.4", default-features = false }
|
||||||
tauri = { version = "^2.11" }
|
tauri = { version = "^2.11" }
|
||||||
tauri-build = { version = "^2.6" }
|
tauri-build = { version = "^2.6" }
|
||||||
tauri-plugin-tracing = { version = "^0.3" }
|
tauri-plugin-tracing = { version = "^0.3" }
|
||||||
ts-rs = { version = "^12.0" }
|
ts-rs = { version = "^12.0" }
|
||||||
|
zeroize = { version = "^1.9" }
|
||||||
|
|
||||||
[workspace.lints.rust]
|
[workspace.lints.rust]
|
||||||
missing_docs = "warn"
|
missing_docs = "warn"
|
||||||
|
|||||||
12
ROADMAP.md
12
ROADMAP.md
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: ROADMAP.md -->
|
<!-- file: ROADMAP.md -->
|
||||||
<!-- version: 37 -->
|
<!-- version: 55 -->
|
||||||
|
|
||||||
# Roadmap KSP
|
# Roadmap KSP
|
||||||
|
|
||||||
@@ -47,15 +47,15 @@ Le roadmap décrit les objectifs à atteindre et les grandes étapes prévues. U
|
|||||||
|
|
||||||
- [X] `0.2.1` — **HTTP transport foundation réduite par le gate `pre.001`** : crate/settings/JSON-RPC/registry 52 current + 14 deprecated historiques, pool/rôles/limites/retry, Config adapter, documentation et 4 méthodes typées canari (`getBalance`, `getGenesisHash`, `getHealth`, `getVersion`) publiés stables.
|
- [X] `0.2.1` — **HTTP transport foundation réduite par le gate `pre.001`** : crate/settings/JSON-RPC/registry 52 current + 14 deprecated historiques, pool/rôles/limites/retry, Config adapter, documentation et 4 méthodes typées canari (`getBalance`, `getGenesisHash`, `getHealth`, `getVersion`) publiés stables.
|
||||||
- [X] `0.2.2` — HTTP Accounts + Tokens + Cluster : 22 wrappers typés (5 Accounts + 5 Tokens + 12 Cluster), canaries de complétude 52+14, smoke Devnet Transport pur et smoke historique Config -> Transport validés, documentation durable et prompt `0.2.3` publiés stables.
|
- [X] `0.2.2` — HTTP Accounts + Tokens + Cluster : 22 wrappers typés (5 Accounts + 5 Tokens + 12 Cluster), canaries de complétude 52+14, smoke Devnet Transport pur et smoke historique Config -> Transport validés, documentation durable et prompt `0.2.3` publiés stables.
|
||||||
- [ ] `0.2.3` — Compléter les 11 méthodes HTTP Transactions, y compris write/submission technique avec politique no-resend ambigu.
|
- [X] `0.2.3` — HTTP Transactions stable : 11/11 wrappers typés publiés, classification `8 Read / 2 WriteSubmission / 1 Simulation`, no-resend ambigu prouvé pour les write submissions, `KSP-TRANSPORT-007` réaudité conforme sur les 37 wrappers HTTP courants, graphes Cargo et deux smokes Devnet validés ; `0.2.4` reprend les 15 Blocks/Economics restants.
|
||||||
- [ ] `0.2.4` — Compléter les 10 méthodes HTTP Blocks + 5 Economics et exécuter la compliance finale de toute la surface HTTP 52 current + 14 deprecated historiques.
|
- [X] `0.2.4` — HTTP Blocks + Economics stable : 15/15 wrappers `V0_2_4` publiés, surface typed complète à 52/52 méthodes courantes, 14/14 historiques conservées, réaudit SIMD/inventaire final et `KSP-TRANSPORT-007` global validés ; deux smokes Devnet passés avant publication.
|
||||||
- [ ] `0.2.5` — Introduire `ksp-wallet-lib`, le format `.kspwallet`, la gestion sûre des secrets et une architecture d'import/export extensible ; exclure `WalletPolicy`.
|
- [X] `0.2.5` — Wallet foundation stable : `.kspwallet` V1, VIEW/OWNER indépendants, Argon2id/XChaCha20-Poly1305, autorité Ed25519 OWNER, persistence no-clobber, signature, administration/rotations/révocation VIEW forte, import/export Solana CLI JSON + Base58, canaris adversariaux, interop externe et documentation durable publiés. La clôture `pre.010-fix.001`–`fix.003` ajoute `ed25519-dalek 3.0.0` direct, normalise le Rust workspace et installe l’audit structurel Python complémentaire à rustfmt/Clippy. `Pubkey` reste via `ksp-core-lib`, la keypair reste encapsulée dans Wallet et Config/Transport/ExecutionPolicy/Store/Tauri restent hors Wallet.
|
||||||
- [ ] `0.2.6` — Introduire `ksp-app-wallet-desk` utilisant Config composite + Wallet + transport HTTP, notamment pour afficher l'identité et le solde d'un wallet.
|
- [ ] `0.2.6` — Introduire `ksp-app-wallet-desk` utilisant Config composite + Wallet + transport HTTP, notamment pour afficher l'identité et le solde d'un wallet ; le prompt de démarrage est préparé par `0.2.5-pre.010`.
|
||||||
- [ ] `0.2.7` — Étendre `ksp-onchain-transport-lib` au WebSocket Solana standard complet ; permettre plusieurs sessions sur une même URL sans imposer encore un pool automatique complexe.
|
- [ ] `0.2.7` — Étendre `ksp-onchain-transport-lib` au WebSocket Solana standard complet ; permettre plusieurs sessions sur une même URL sans imposer encore un pool automatique complexe.
|
||||||
- [ ] `0.2.8` — Ajouter Helius LaserStream WebSocket comme extension du moteur WebSocket standard, sans duplication de client.
|
- [ ] `0.2.8` — Ajouter Helius LaserStream WebSocket comme extension du moteur WebSocket standard, sans duplication de client.
|
||||||
- [ ] `0.2.9` — Ajouter une première fondation Yellowstone gRPC standard/provider-neutral ; dimensionner la surface exacte à `pre.001` selon la documentation normative actuelle.
|
- [ ] `0.2.9` — Ajouter une première fondation Yellowstone gRPC standard/provider-neutral ; dimensionner la surface exacte à `pre.001` selon la documentation normative actuelle.
|
||||||
- [ ] `0.2.10` — Introduire `ksp-offchain-transport-lib` avec un premier lecteur de prix, au minimum SOL/USD et SOL/EUR.
|
- [ ] `0.2.10` — Introduire `ksp-offchain-transport-lib` avec un premier lecteur de prix, au minimum SOL/USD et SOL/EUR.
|
||||||
- [ ] `0.2.11` — Introduire une petite application desk de visualisation des prix.
|
- [ ] `0.2.11` — Introduire une petite application desk de visualisation/validation des prix offchain, puis intégrer cette capacité dans `ksp-app-wallet-desk` sans dupliquer la logique de récupération/normalisation possédée par le composant spécialisé.
|
||||||
- [ ] `0.2.12` — Introduire la première surface de `ksp-interface-lib`, comprenant une API wire publique utilisable par les implémentations officielles et externes.
|
- [ ] `0.2.12` — Introduire la première surface de `ksp-interface-lib`, comprenant une API wire publique utilisable par les implémentations officielles et externes.
|
||||||
- [ ] `0.2.13` — Introduire `ksp-program-api` comme premier contrat Program extensible, sans imposer encore `ksp-program-lib` complet.
|
- [ ] `0.2.13` — Introduire `ksp-program-api` comme premier contrat Program extensible, sans imposer encore `ksp-program-lib` complet.
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,8 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/app_state.rs
|
// file: crates/ksp-app-config-desk/src/app_state.rs
|
||||||
// version: 6
|
// version: 8
|
||||||
|
|
||||||
//! Shared backend state owned by the Tauri application.
|
//! Shared backend state owned by the Tauri application.
|
||||||
|
|
||||||
struct LoggingRuntimeState {
|
|
||||||
guard: ksp_logging_lib::LoggingGuard,
|
|
||||||
active_profile_id: std::option::Option<String>,
|
|
||||||
selection_source: String,
|
|
||||||
generation: u32,
|
|
||||||
fallback_active: bool,
|
|
||||||
startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Shared Config Desk application state managed by Tauri.
|
/// Shared Config Desk application state managed by Tauri.
|
||||||
pub(crate) struct AppState {
|
pub(crate) struct AppState {
|
||||||
config_management: ksp_config_lib::ConfigManagement,
|
config_management: ksp_config_lib::ConfigManagement,
|
||||||
@@ -28,7 +19,7 @@ impl AppState {
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let runtime_identity = crate::logging_runtime::launch_identity();
|
let runtime_identity = crate::launch_identity();
|
||||||
let runtime_identity = match runtime_identity {
|
let runtime_identity = match runtime_identity {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
@@ -175,7 +166,7 @@ impl AppState {
|
|||||||
let dropped = runtime.guard.dropped_lines();
|
let dropped = runtime.guard.dropped_lines();
|
||||||
let mut files = std::vec::Vec::<crate::LoggingRuntimeFileDto>::new();
|
let mut files = std::vec::Vec::<crate::LoggingRuntimeFileDto>::new();
|
||||||
for metadata in runtime.guard.active_file_outputs() {
|
for metadata in runtime.guard.active_file_outputs() {
|
||||||
files.push(crate::logging_runtime::project_file(&metadata));
|
files.push(crate::project_logging_file(&metadata));
|
||||||
}
|
}
|
||||||
return std::result::Result::Ok(crate::LoggingRuntimeStatusDto {
|
return std::result::Result::Ok(crate::LoggingRuntimeStatusDto {
|
||||||
active_profile: runtime.active_profile_id.clone(),
|
active_profile: runtime.active_profile_id.clone(),
|
||||||
@@ -185,9 +176,9 @@ impl AppState {
|
|||||||
application_id: identity.application_id().to_owned(),
|
application_id: identity.application_id().to_owned(),
|
||||||
launch_timestamp: identity.launch_timestamp().to_owned(),
|
launch_timestamp: identity.launch_timestamp().to_owned(),
|
||||||
console_enabled,
|
console_enabled,
|
||||||
dropped_console_lines: crate::logging_runtime::count_to_u64(dropped.console()),
|
dropped_console_lines: crate::count_to_u64(dropped.console()),
|
||||||
dropped_file_lines: crate::logging_runtime::count_to_u64(dropped.file()),
|
dropped_file_lines: crate::count_to_u64(dropped.file()),
|
||||||
dropped_total_lines: crate::logging_runtime::count_to_u64(dropped.total()),
|
dropped_total_lines: crate::count_to_u64(dropped.total()),
|
||||||
files,
|
files,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -206,3 +197,12 @@ impl AppState {
|
|||||||
.is_ok();
|
.is_ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LoggingRuntimeState {
|
||||||
|
guard: ksp_logging_lib::LoggingGuard,
|
||||||
|
active_profile_id: std::option::Option<String>,
|
||||||
|
selection_source: String,
|
||||||
|
generation: u32,
|
||||||
|
fallback_active: bool,
|
||||||
|
startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,13 +1,19 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/bootstrap.rs
|
// file: crates/ksp-app-config-desk/src/bootstrap.rs
|
||||||
// version: 3
|
// version: 6
|
||||||
|
|
||||||
//! Config and Logging bootstrap for the desktop application.
|
//! Config and Logging bootstrap for the desktop application.
|
||||||
|
|
||||||
|
/// Crate-internal `LoggingStartup` state shared across the owning crate.
|
||||||
pub(crate) struct LoggingStartup {
|
pub(crate) struct LoggingStartup {
|
||||||
|
/// Stores the guard value for this state.
|
||||||
pub(crate) guard: ksp_logging_lib::LoggingGuard,
|
pub(crate) guard: ksp_logging_lib::LoggingGuard,
|
||||||
|
/// Stores the active profile id value for this state.
|
||||||
pub(crate) active_profile_id: std::option::Option<String>,
|
pub(crate) active_profile_id: std::option::Option<String>,
|
||||||
|
/// Stores the selection source value for this state.
|
||||||
pub(crate) selection_source: String,
|
pub(crate) selection_source: String,
|
||||||
|
/// Stores the fallback active value for this state.
|
||||||
pub(crate) fallback_active: bool,
|
pub(crate) fallback_active: bool,
|
||||||
|
/// Stores the startup diagnostic value for this state.
|
||||||
pub(crate) startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
|
pub(crate) startup_diagnostic: std::option::Option<crate::CommandErrorDto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23,6 +29,7 @@ enum LoggingStartupPlan {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the crate-internal config management operation for the owning module.
|
||||||
pub(crate) fn config_management(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<ksp_config_lib::ConfigManagement> {
|
pub(crate) fn config_management(arguments: &[std::ffi::OsString]) -> ksp_core_lib::Result<ksp_config_lib::ConfigManagement> {
|
||||||
let bootstrap = ksp_config_lib::ConfigBootstrapOptions::from_args(arguments);
|
let bootstrap = ksp_config_lib::ConfigBootstrapOptions::from_args(arguments);
|
||||||
let bootstrap = match bootstrap {
|
let bootstrap = match bootstrap {
|
||||||
@@ -38,10 +45,11 @@ pub(crate) fn config_management(arguments: &[std::ffi::OsString]) -> ksp_core_li
|
|||||||
return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(engine));
|
return std::result::Result::Ok(ksp_config_lib::ConfigManagement::new(engine));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the crate-internal initialize logging operation for the owning module.
|
||||||
pub(crate) fn initialize_logging(
|
pub(crate) fn initialize_logging(
|
||||||
management: &ksp_config_lib::ConfigManagement,
|
management: &ksp_config_lib::ConfigManagement,
|
||||||
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
|
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
|
||||||
) -> ksp_core_lib::Result<LoggingStartup> {
|
) -> ksp_core_lib::Result<crate::LoggingStartup> {
|
||||||
let plan = resolve_logging_startup(management);
|
let plan = resolve_logging_startup(management);
|
||||||
return match plan {
|
return match plan {
|
||||||
LoggingStartupPlan::Managed { active_profile_id, settings } => {
|
LoggingStartupPlan::Managed { active_profile_id, settings } => {
|
||||||
@@ -71,6 +79,15 @@ pub(crate) fn initialize_logging(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn fallback_logging_settings() -> ksp_logging_lib::LoggingSettings {
|
||||||
|
return ksp_logging_lib::LoggingSettings::new(
|
||||||
|
ksp_logging_lib::LogFilterLevel::Info,
|
||||||
|
ksp_logging_lib::SpanEvents::Off,
|
||||||
|
std::option::Option::Some(ksp_logging_lib::ConsoleSettings::stderr()),
|
||||||
|
std::vec::Vec::new(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_logging_startup(management: &ksp_config_lib::ConfigManagement) -> LoggingStartupPlan {
|
fn resolve_logging_startup(management: &ksp_config_lib::ConfigManagement) -> LoggingStartupPlan {
|
||||||
let environment = ksp_config_lib::ConfigEnvironment::load();
|
let environment = ksp_config_lib::ConfigEnvironment::load();
|
||||||
let environment = match environment {
|
let environment = match environment {
|
||||||
@@ -93,7 +110,7 @@ fn fallback_startup_plan(initial_error: ksp_core_lib::Error) -> LoggingStartupPl
|
|||||||
fn initialize_fallback_logging(
|
fn initialize_fallback_logging(
|
||||||
initial_error: ksp_core_lib::Error,
|
initial_error: ksp_core_lib::Error,
|
||||||
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
|
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
|
||||||
) -> ksp_core_lib::Result<LoggingStartup> {
|
) -> ksp_core_lib::Result<crate::LoggingStartup> {
|
||||||
let diagnostic = crate::CommandErrorDto::from_error(&initial_error);
|
let diagnostic = crate::CommandErrorDto::from_error(&initial_error);
|
||||||
return initialize_planned_fallback_logging(initial_error, diagnostic, fallback_logging_settings(), runtime_identity);
|
return initialize_planned_fallback_logging(initial_error, diagnostic, fallback_logging_settings(), runtime_identity);
|
||||||
}
|
}
|
||||||
@@ -103,7 +120,7 @@ fn initialize_planned_fallback_logging(
|
|||||||
diagnostic: crate::CommandErrorDto,
|
diagnostic: crate::CommandErrorDto,
|
||||||
settings: ksp_logging_lib::LoggingSettings,
|
settings: ksp_logging_lib::LoggingSettings,
|
||||||
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
|
runtime_identity: &ksp_logging_lib::LoggingRuntimeIdentity,
|
||||||
) -> ksp_core_lib::Result<LoggingStartup> {
|
) -> ksp_core_lib::Result<crate::LoggingStartup> {
|
||||||
let guard = ksp_logging_lib::initialize_with_identity(&settings, runtime_identity);
|
let guard = ksp_logging_lib::initialize_with_identity(&settings, runtime_identity);
|
||||||
let guard = match guard {
|
let guard = match guard {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -132,15 +149,6 @@ fn initialize_planned_fallback_logging(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn fallback_logging_settings() -> ksp_logging_lib::LoggingSettings {
|
|
||||||
return ksp_logging_lib::LoggingSettings::new(
|
|
||||||
ksp_logging_lib::LogFilterLevel::Info,
|
|
||||||
ksp_logging_lib::SpanEvents::Off,
|
|
||||||
std::option::Option::Some(ksp_logging_lib::ConsoleSettings::stderr()),
|
|
||||||
std::vec::Vec::new(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[path = "../unit_tests/bootstrap.rs"]
|
#[path = "../unit_tests/bootstrap.rs"]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|||||||
@@ -1,8 +1,28 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/constants.rs
|
// file: crates/ksp-app-config-desk/src/constants.rs
|
||||||
// version: 9
|
// version: 10
|
||||||
|
|
||||||
//! Application-owned tracing targets and domains.
|
//! Application-owned tracing targets and domains.
|
||||||
|
|
||||||
|
/// Structured domain used while bootstrapping Config and Logging.
|
||||||
|
pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "config.bootstrap";
|
||||||
|
/// Structured domain for Config document inventory, diagnostics and repair.
|
||||||
|
pub(crate) const TRACING_DOMAIN_DOCUMENTS: &str = "config.documents";
|
||||||
|
/// Structured domain for safe Config environment reports.
|
||||||
|
pub(crate) const TRACING_DOMAIN_ENVIRONMENT: &str = "config.environment";
|
||||||
|
/// Structured domain used by technical frontend events.
|
||||||
|
pub(crate) const TRACING_DOMAIN_FRONTEND: &str = "frontend";
|
||||||
|
/// Structured domain for typed Logging editor inspection.
|
||||||
|
pub(crate) const TRACING_DOMAIN_LOGGING_EDITOR: &str = "config.logging_editor";
|
||||||
|
/// Structured domain for active Logging runtime metadata and explicit profile application.
|
||||||
|
pub(crate) const TRACING_DOMAIN_LOGGING_RUNTIME: &str = "config.logging_runtime";
|
||||||
|
/// Structured domain used by controlled Logging test events.
|
||||||
|
pub(crate) const TRACING_DOMAIN_LOGGING_TEST: &str = "config.logging_test";
|
||||||
|
/// Structured domain for Config profile selection and provenance inspection.
|
||||||
|
pub(crate) const TRACING_DOMAIN_PROFILES: &str = "config.profiles";
|
||||||
|
/// Structured domain for explicit privileged Secret reveal operations.
|
||||||
|
pub(crate) const TRACING_DOMAIN_SECRETS: &str = "config.secrets";
|
||||||
|
/// Structured domain used by Tauri window lifecycle operations.
|
||||||
|
pub(crate) const TRACING_DOMAIN_WINDOWS: &str = "desktop.window";
|
||||||
/// Owning target for backend events emitted by Config Desk.
|
/// Owning target for backend events emitted by Config Desk.
|
||||||
pub(crate) const TRACING_TARGET: &str = "ksp-app-config-desk";
|
pub(crate) const TRACING_TARGET: &str = "ksp-app-config-desk";
|
||||||
/// Owning target for generic frontend events emitted through the KSP bridge.
|
/// Owning target for generic frontend events emitted through the KSP bridge.
|
||||||
@@ -13,23 +33,3 @@ pub(crate) const TRACING_TARGET_FRONTEND_MAIN: &str = "ksp-app-config-desk.front
|
|||||||
pub(crate) const TRACING_TARGET_FRONTEND_SPLASH: &str = "ksp-app-config-desk.frontend.splash";
|
pub(crate) const TRACING_TARGET_FRONTEND_SPLASH: &str = "ksp-app-config-desk.frontend.splash";
|
||||||
/// Dedicated target used by the controlled Logging test panel.
|
/// Dedicated target used by the controlled Logging test panel.
|
||||||
pub(crate) const TRACING_TARGET_LOGGING_TEST: &str = "ksp-app-config-desk.logging-test";
|
pub(crate) const TRACING_TARGET_LOGGING_TEST: &str = "ksp-app-config-desk.logging-test";
|
||||||
/// Structured domain used while bootstrapping Config and Logging.
|
|
||||||
pub(crate) const TRACING_DOMAIN_BOOTSTRAP: &str = "config.bootstrap";
|
|
||||||
/// Structured domain used by technical frontend events.
|
|
||||||
pub(crate) const TRACING_DOMAIN_FRONTEND: &str = "frontend";
|
|
||||||
/// Structured domain used by Tauri window lifecycle operations.
|
|
||||||
pub(crate) const TRACING_DOMAIN_WINDOWS: &str = "desktop.window";
|
|
||||||
/// Structured domain for Config document inventory, diagnostics and repair.
|
|
||||||
pub(crate) const TRACING_DOMAIN_DOCUMENTS: &str = "config.documents";
|
|
||||||
/// Structured domain for Config profile selection and provenance inspection.
|
|
||||||
pub(crate) const TRACING_DOMAIN_PROFILES: &str = "config.profiles";
|
|
||||||
/// Structured domain for safe Config environment reports.
|
|
||||||
pub(crate) const TRACING_DOMAIN_ENVIRONMENT: &str = "config.environment";
|
|
||||||
/// Structured domain for explicit privileged Secret reveal operations.
|
|
||||||
pub(crate) const TRACING_DOMAIN_SECRETS: &str = "config.secrets";
|
|
||||||
/// Structured domain for typed Logging editor inspection.
|
|
||||||
pub(crate) const TRACING_DOMAIN_LOGGING_EDITOR: &str = "config.logging_editor";
|
|
||||||
/// Structured domain for active Logging runtime metadata and explicit profile application.
|
|
||||||
pub(crate) const TRACING_DOMAIN_LOGGING_RUNTIME: &str = "config.logging_runtime";
|
|
||||||
/// Structured domain used by controlled Logging test events.
|
|
||||||
pub(crate) const TRACING_DOMAIN_LOGGING_TEST: &str = "config.logging_test";
|
|
||||||
|
|||||||
@@ -1,19 +1,19 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/documents.rs
|
// file: crates/ksp-app-config-desk/src/documents.rs
|
||||||
// version: 1
|
// version: 3
|
||||||
|
|
||||||
//! Generic Config document inventory, diagnostics and validated repair services.
|
//! Generic Config document inventory, diagnostics and validated repair services.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
const STATUS_VALID: &str = "valid";
|
const STAGE_EFFECTIVE: &str = "effective";
|
||||||
const STATUS_INVALID: &str = "invalid";
|
|
||||||
const STAGE_VALID: &str = "valid";
|
|
||||||
const STAGE_READ: &str = "read";
|
|
||||||
const STAGE_JSON: &str = "json";
|
const STAGE_JSON: &str = "json";
|
||||||
|
const STAGE_OTHER: &str = "other";
|
||||||
|
const STAGE_READ: &str = "read";
|
||||||
const STAGE_SCHEMA: &str = "schema";
|
const STAGE_SCHEMA: &str = "schema";
|
||||||
const STAGE_SEMANTIC: &str = "semantic";
|
const STAGE_SEMANTIC: &str = "semantic";
|
||||||
const STAGE_EFFECTIVE: &str = "effective";
|
const STAGE_VALID: &str = "valid";
|
||||||
const STAGE_OTHER: &str = "other";
|
const STATUS_INVALID: &str = "invalid";
|
||||||
|
const STATUS_VALID: &str = "valid";
|
||||||
|
|
||||||
/// Safe inventory row for one Config document registered by `ksp-config-lib`.
|
/// Safe inventory row for one Config document registered by `ksp-config-lib`.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
@@ -78,7 +78,7 @@ impl ConfigDocumentErrorDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Lists all Config-kind documents from the Config registry and evaluates their current backend validation state.
|
/// Lists all Config-kind documents from the Config registry and evaluates their current backend validation state.
|
||||||
pub(crate) fn inventory(state: &crate::AppState) -> std::vec::Vec<ConfigDocumentSummaryDto> {
|
pub(crate) fn document_inventory(state: &crate::AppState) -> std::vec::Vec<ConfigDocumentSummaryDto> {
|
||||||
let management = state.config_management();
|
let management = state.config_management();
|
||||||
let engine = management.engine();
|
let engine = management.engine();
|
||||||
let mut documents = std::vec::Vec::new();
|
let mut documents = std::vec::Vec::new();
|
||||||
@@ -98,7 +98,7 @@ pub(crate) fn inventory(state: &crate::AppState) -> std::vec::Vec<ConfigDocument
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads one Config document detail, including raw source when Config can read it.
|
/// Loads one Config document detail, including raw source when Config can read it.
|
||||||
pub(crate) fn detail(state: &crate::AppState, file_id_text: &str) -> std::result::Result<ConfigDocumentDetailDto, ConfigDocumentErrorDto> {
|
pub(crate) fn document_detail(state: &crate::AppState, file_id_text: &str) -> std::result::Result<ConfigDocumentDetailDto, ConfigDocumentErrorDto> {
|
||||||
let file_id = parse_config_file_id(file_id_text);
|
let file_id = parse_config_file_id(file_id_text);
|
||||||
let file_id = match file_id {
|
let file_id = match file_id {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -133,7 +133,7 @@ pub(crate) fn detail(state: &crate::AppState, file_id_text: &str) -> std::result
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Validates and atomically persists one raw Config source candidate, then reloads the document detail.
|
/// Validates and atomically persists one raw Config source candidate, then reloads the document detail.
|
||||||
pub(crate) fn save_source(
|
pub(crate) fn save_document_source(
|
||||||
state: &crate::AppState,
|
state: &crate::AppState,
|
||||||
file_id_text: &str,
|
file_id_text: &str,
|
||||||
source: &str,
|
source: &str,
|
||||||
@@ -148,7 +148,7 @@ pub(crate) fn save_source(
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(ConfigDocumentErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => return std::result::Result::Err(ConfigDocumentErrorDto::from_error(&error)),
|
||||||
};
|
};
|
||||||
let document = detail(state, file_id.as_str());
|
let document = crate::document_detail(state, file_id.as_str());
|
||||||
let document = match document {
|
let document = match document {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/dto_common.rs
|
// file: crates/ksp-app-config-desk/src/dto_common.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Common Tauri DTOs shared by Config Desk commands.
|
//! Common Tauri DTOs shared by Config Desk commands.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// Safe command error projection that never serializes arbitrary KSP error context or source values.
|
/// Safe command error projection that never serializes arbitrary KSP error context or source values.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/environment.rs
|
// file: crates/ksp-app-config-desk/src/environment.rs
|
||||||
// version: 2
|
// version: 4
|
||||||
|
|
||||||
//! Safe Config environment reports and `.env` management projections for Config Desk.
|
//! Safe Config environment reports and `.env` management projections for Config Desk.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// Safe desired/effective environment view exposed to the ordinary Config Desk frontend.
|
/// Safe desired/effective environment view exposed to the ordinary Config Desk frontend.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
@@ -63,17 +63,17 @@ impl EnvironmentMutationOperation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the safe environment report owned by Config.
|
/// Returns the safe environment report owned by Config.
|
||||||
pub(crate) fn report(state: &crate::AppState) -> ksp_core_lib::Result<std::vec::Vec<ConfigEnvironmentReportDto>> {
|
pub(crate) fn environment_report(state: &crate::AppState) -> ksp_core_lib::Result<std::vec::Vec<ConfigEnvironmentReportDto>> {
|
||||||
return report_from_management(state.config_management());
|
return report_from_management(state.config_management());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates or replaces one `.env` entry exclusively through ConfigManagement.
|
/// Creates or replaces one `.env` entry exclusively through ConfigManagement.
|
||||||
pub(crate) fn set_value(state: &crate::AppState, variable_name: &str, value: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeDto> {
|
pub(crate) fn set_environment_value(state: &crate::AppState, variable_name: &str, value: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeDto> {
|
||||||
return set_value_from_management(state.config_management(), variable_name, value);
|
return set_value_from_management(state.config_management(), variable_name, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Removes one `.env` entry exclusively through ConfigManagement.
|
/// Removes one `.env` entry exclusively through ConfigManagement.
|
||||||
pub(crate) fn remove_value(state: &crate::AppState, variable_name: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeDto> {
|
pub(crate) fn remove_environment_value(state: &crate::AppState, variable_name: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeDto> {
|
||||||
return remove_value_from_management(state.config_management(), variable_name);
|
return remove_value_from_management(state.config_management(), variable_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,44 +1,42 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/errors.rs
|
// file: crates/ksp-app-config-desk/src/errors.rs
|
||||||
// version: 9
|
// version: 10
|
||||||
|
|
||||||
//! Application-local error codes for the configuration desktop shell.
|
//! Application-local error codes for the configuration desktop shell.
|
||||||
|
|
||||||
/// Tauri runtime assembly or execution failed.
|
/// Shared Config Desk application state is internally inconsistent.
|
||||||
pub(crate) const ERROR_CODE_TAURI_RUNTIME_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "tauri_runtime_failed");
|
pub(crate) const ERROR_CODE_APP_STATE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "app_state_invalid");
|
||||||
|
/// Shared Config Desk runtime state cannot be locked safely.
|
||||||
|
pub(crate) const ERROR_CODE_APP_STATE_LOCK_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "app_state_lock_failed");
|
||||||
|
/// A Documents command requested a registered file that is not a Config-kind document.
|
||||||
|
pub(crate) const ERROR_CODE_DOCUMENT_KIND_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "document_kind_invalid");
|
||||||
|
/// Frontend logging requested an unsupported level.
|
||||||
|
pub(crate) const ERROR_CODE_FRONTEND_LOG_LEVEL_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "frontend_log_level_invalid");
|
||||||
|
/// Frontend logging requested a target outside the application whitelist.
|
||||||
|
pub(crate) const ERROR_CODE_FRONTEND_LOG_TARGET_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "frontend_log_target_invalid");
|
||||||
/// Config Desk could not install the managed Logging runtime or its safe fallback.
|
/// Config Desk could not install the managed Logging runtime or its safe fallback.
|
||||||
pub(crate) const ERROR_CODE_LOGGING_BOOTSTRAP_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "logging_bootstrap_failed");
|
pub(crate) const ERROR_CODE_LOGGING_BOOTSTRAP_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "logging_bootstrap_failed");
|
||||||
/// Config Desk persisted a Logging candidate but could not restore the previous source after runtime application failed.
|
/// Config Desk persisted a Logging candidate but could not restore the previous source after runtime application failed.
|
||||||
pub(crate) const ERROR_CODE_LOGGING_SOURCE_ROLLBACK_FAILED: ksp_core_lib::ErrorCode =
|
pub(crate) const ERROR_CODE_LOGGING_SOURCE_ROLLBACK_FAILED: ksp_core_lib::ErrorCode =
|
||||||
ksp_core_lib::ErrorCode::new("config_desk", "logging_source_rollback_failed");
|
ksp_core_lib::ErrorCode::new("config_desk", "logging_source_rollback_failed");
|
||||||
/// Shared Config Desk application state is internally inconsistent.
|
|
||||||
pub(crate) const ERROR_CODE_APP_STATE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "app_state_invalid");
|
|
||||||
/// Shared Config Desk runtime state cannot be locked safely.
|
|
||||||
pub(crate) const ERROR_CODE_APP_STATE_LOCK_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "app_state_lock_failed");
|
|
||||||
/// Frontend logging requested an unsupported level.
|
|
||||||
pub(crate) const ERROR_CODE_FRONTEND_LOG_LEVEL_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "frontend_log_level_invalid");
|
|
||||||
/// Frontend logging requested a target outside the application whitelist.
|
|
||||||
pub(crate) const ERROR_CODE_FRONTEND_LOG_TARGET_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "frontend_log_target_invalid");
|
|
||||||
/// Controlled Logging test request contains an unsupported or invalid selector.
|
/// Controlled Logging test request contains an unsupported or invalid selector.
|
||||||
pub(crate) const ERROR_CODE_LOGGING_TEST_REQUEST_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "logging_test_request_invalid");
|
pub(crate) const ERROR_CODE_LOGGING_TEST_REQUEST_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "logging_test_request_invalid");
|
||||||
|
|
||||||
/// A validated Config document does not expose the standard profile contract expected by the Profiles panel.
|
/// A validated Config document does not expose the standard profile contract expected by the Profiles panel.
|
||||||
pub(crate) const ERROR_CODE_PROFILE_CONTRACT_MISSING: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "profile_contract_missing");
|
pub(crate) const ERROR_CODE_PROFILE_CONTRACT_MISSING: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "profile_contract_missing");
|
||||||
/// Config profile data could not be projected safely for the frontend.
|
/// Config profile data could not be projected safely for the frontend.
|
||||||
pub(crate) const ERROR_CODE_PROFILE_PROJECTION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "profile_projection_failed");
|
pub(crate) const ERROR_CODE_PROFILE_PROJECTION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "profile_projection_failed");
|
||||||
/// A KSP desk splash environment duration is malformed or exceeds its safety bound.
|
|
||||||
pub(crate) const ERROR_CODE_SPLASH_SETTING_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "splash_setting_invalid");
|
|
||||||
/// Splash readiness was invoked from a window other than the splash window.
|
|
||||||
pub(crate) const ERROR_CODE_SPLASH_ORIGIN_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "splash_origin_invalid");
|
|
||||||
/// A required Tauri window is missing from the configured application runtime.
|
|
||||||
pub(crate) const ERROR_CODE_TAURI_WINDOW_MISSING: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "tauri_window_missing");
|
|
||||||
/// A Tauri window show/focus/destroy/event operation failed.
|
|
||||||
pub(crate) const ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED: ksp_core_lib::ErrorCode =
|
|
||||||
ksp_core_lib::ErrorCode::new("config_desk", "tauri_window_operation_failed");
|
|
||||||
|
|
||||||
/// A Documents command requested a registered file that is not a Config-kind document.
|
|
||||||
pub(crate) const ERROR_CODE_DOCUMENT_KIND_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "document_kind_invalid");
|
|
||||||
/// Privileged reveal was requested for a non-Secret KSP/KSPB namespace.
|
/// Privileged reveal was requested for a non-Secret KSP/KSPB namespace.
|
||||||
pub(crate) const ERROR_CODE_SECRET_REVEAL_REQUIRES_SECRET: ksp_core_lib::ErrorCode =
|
pub(crate) const ERROR_CODE_SECRET_REVEAL_REQUIRES_SECRET: ksp_core_lib::ErrorCode =
|
||||||
ksp_core_lib::ErrorCode::new("config_desk", "secret_reveal_requires_secret");
|
ksp_core_lib::ErrorCode::new("config_desk", "secret_reveal_requires_secret");
|
||||||
/// Privileged reveal requested an unsupported source selector.
|
/// Privileged reveal requested an unsupported source selector.
|
||||||
pub(crate) const ERROR_CODE_SECRET_REVEAL_SOURCE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "secret_reveal_source_invalid");
|
pub(crate) const ERROR_CODE_SECRET_REVEAL_SOURCE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "secret_reveal_source_invalid");
|
||||||
|
/// Splash readiness was invoked from a window other than the splash window.
|
||||||
|
pub(crate) const ERROR_CODE_SPLASH_ORIGIN_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "splash_origin_invalid");
|
||||||
|
/// A KSP desk splash environment duration is malformed or exceeds its safety bound.
|
||||||
|
pub(crate) const ERROR_CODE_SPLASH_SETTING_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "splash_setting_invalid");
|
||||||
|
/// Tauri runtime assembly or execution failed.
|
||||||
|
pub(crate) const ERROR_CODE_TAURI_RUNTIME_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "tauri_runtime_failed");
|
||||||
|
/// A required Tauri window is missing from the configured application runtime.
|
||||||
|
pub(crate) const ERROR_CODE_TAURI_WINDOW_MISSING: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config_desk", "tauri_window_missing");
|
||||||
|
/// A Tauri window show/focus/destroy/event operation failed.
|
||||||
|
pub(crate) const ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED: ksp_core_lib::ErrorCode =
|
||||||
|
ksp_core_lib::ErrorCode::new("config_desk", "tauri_window_operation_failed");
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/frontend_logging.rs
|
// file: crates/ksp-app-config-desk/src/frontend_logging.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! KSP-owned bridge for technical log events emitted by Config Desk frontend scripts.
|
//! KSP-owned bridge for technical log events emitted by Config Desk frontend scripts.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// Log payload sent by Config Desk frontend scripts.
|
/// Log payload sent by Config Desk frontend scripts.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, TS)]
|
#[derive(Clone, Debug, serde::Deserialize, TS)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/lib.rs
|
// file: crates/ksp-app-config-desk/src/lib.rs
|
||||||
// version: 14
|
// version: 16
|
||||||
|
|
||||||
//! Tauri desktop application for managing and validating KSP configuration.
|
//! Tauri desktop application for managing and validating KSP configuration.
|
||||||
|
|
||||||
@@ -28,61 +28,173 @@ mod tw_splash;
|
|||||||
/// Runs the KSP configuration desktop application.
|
/// Runs the KSP configuration desktop application.
|
||||||
pub use self::tauri::run;
|
pub use self::tauri::run;
|
||||||
|
|
||||||
|
/// Shared Config Desk application state managed by Tauri.
|
||||||
pub(crate) use self::app_state::AppState;
|
pub(crate) use self::app_state::AppState;
|
||||||
|
/// Crate-internal `LoggingStartup` state shared across the owning crate.
|
||||||
|
pub(crate) use self::bootstrap::LoggingStartup;
|
||||||
|
/// Executes the crate-internal config management operation for the owning module.
|
||||||
pub(crate) use self::bootstrap::config_management;
|
pub(crate) use self::bootstrap::config_management;
|
||||||
|
/// Executes the crate-internal initialize logging operation for the owning module.
|
||||||
pub(crate) use self::bootstrap::initialize_logging;
|
pub(crate) use self::bootstrap::initialize_logging;
|
||||||
|
/// Structured domain used while bootstrapping Config and Logging.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
|
pub(crate) use self::constants::TRACING_DOMAIN_BOOTSTRAP;
|
||||||
|
/// Structured domain for Config document inventory, diagnostics and repair.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_DOCUMENTS;
|
pub(crate) use self::constants::TRACING_DOMAIN_DOCUMENTS;
|
||||||
|
/// Structured domain for safe Config environment reports.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_ENVIRONMENT;
|
pub(crate) use self::constants::TRACING_DOMAIN_ENVIRONMENT;
|
||||||
|
/// Structured domain used by technical frontend events.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
|
pub(crate) use self::constants::TRACING_DOMAIN_FRONTEND;
|
||||||
|
/// Structured domain for typed Logging editor inspection.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_EDITOR;
|
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_EDITOR;
|
||||||
|
/// Structured domain for active Logging runtime metadata and explicit profile application.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_RUNTIME;
|
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_RUNTIME;
|
||||||
|
/// Structured domain used by controlled Logging test events.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_TEST;
|
pub(crate) use self::constants::TRACING_DOMAIN_LOGGING_TEST;
|
||||||
|
/// Structured domain for Config profile selection and provenance inspection.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_PROFILES;
|
pub(crate) use self::constants::TRACING_DOMAIN_PROFILES;
|
||||||
|
/// Structured domain for explicit privileged Secret reveal operations.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_SECRETS;
|
pub(crate) use self::constants::TRACING_DOMAIN_SECRETS;
|
||||||
|
/// Structured domain used by Tauri window lifecycle operations.
|
||||||
pub(crate) use self::constants::TRACING_DOMAIN_WINDOWS;
|
pub(crate) use self::constants::TRACING_DOMAIN_WINDOWS;
|
||||||
|
/// Owning target for backend events emitted by Config Desk.
|
||||||
pub(crate) use self::constants::TRACING_TARGET;
|
pub(crate) use self::constants::TRACING_TARGET;
|
||||||
|
/// Owning target for generic frontend events emitted through the KSP bridge.
|
||||||
pub(crate) use self::constants::TRACING_TARGET_FRONTEND;
|
pub(crate) use self::constants::TRACING_TARGET_FRONTEND;
|
||||||
|
/// Owning target for main-window frontend events.
|
||||||
pub(crate) use self::constants::TRACING_TARGET_FRONTEND_MAIN;
|
pub(crate) use self::constants::TRACING_TARGET_FRONTEND_MAIN;
|
||||||
|
/// Owning target for splash-window frontend events.
|
||||||
pub(crate) use self::constants::TRACING_TARGET_FRONTEND_SPLASH;
|
pub(crate) use self::constants::TRACING_TARGET_FRONTEND_SPLASH;
|
||||||
|
/// Dedicated target used by the controlled Logging test panel.
|
||||||
pub(crate) use self::constants::TRACING_TARGET_LOGGING_TEST;
|
pub(crate) use self::constants::TRACING_TARGET_LOGGING_TEST;
|
||||||
|
/// Detailed management view for one registered Config document.
|
||||||
pub(crate) use self::documents::ConfigDocumentDetailDto;
|
pub(crate) use self::documents::ConfigDocumentDetailDto;
|
||||||
|
/// Safe document-specific command error with backend-owned diagnostic classification.
|
||||||
pub(crate) use self::documents::ConfigDocumentErrorDto;
|
pub(crate) use self::documents::ConfigDocumentErrorDto;
|
||||||
|
/// Result of one validated raw-source repair attempt that reached persistence successfully.
|
||||||
pub(crate) use self::documents::ConfigDocumentSaveResultDto;
|
pub(crate) use self::documents::ConfigDocumentSaveResultDto;
|
||||||
|
/// Safe inventory row for one Config document registered by `ksp-config-lib`.
|
||||||
pub(crate) use self::documents::ConfigDocumentSummaryDto;
|
pub(crate) use self::documents::ConfigDocumentSummaryDto;
|
||||||
|
/// Loads one Config document detail, including raw source when Config can read it.
|
||||||
|
pub(crate) use self::documents::document_detail;
|
||||||
|
/// Lists all Config-kind documents from the Config registry and evaluates their current backend validation state.
|
||||||
|
pub(crate) use self::documents::document_inventory;
|
||||||
|
/// Validates and atomically persists one raw Config source candidate, then reloads the document detail.
|
||||||
|
pub(crate) use self::documents::save_document_source;
|
||||||
|
/// Initial application/runtime snapshot exposed to the frontend.
|
||||||
pub(crate) use self::dto_common::AppSnapshotDto;
|
pub(crate) use self::dto_common::AppSnapshotDto;
|
||||||
|
/// Safe command error projection that never serializes arbitrary KSP error context or source values.
|
||||||
pub(crate) use self::dto_common::CommandErrorDto;
|
pub(crate) use self::dto_common::CommandErrorDto;
|
||||||
|
/// Safe result metadata for one `.env` mutation.
|
||||||
pub(crate) use self::environment::ConfigEnvironmentChangeDto;
|
pub(crate) use self::environment::ConfigEnvironmentChangeDto;
|
||||||
|
/// Safe desired/effective environment view exposed to the ordinary Config Desk frontend.
|
||||||
pub(crate) use self::environment::ConfigEnvironmentReportDto;
|
pub(crate) use self::environment::ConfigEnvironmentReportDto;
|
||||||
|
/// Returns the safe environment report owned by Config.
|
||||||
|
pub(crate) use self::environment::environment_report;
|
||||||
|
/// Removes one `.env` entry exclusively through ConfigManagement.
|
||||||
|
pub(crate) use self::environment::remove_environment_value;
|
||||||
|
/// Creates or replaces one `.env` entry exclusively through ConfigManagement.
|
||||||
|
pub(crate) use self::environment::set_environment_value;
|
||||||
|
/// Shared Config Desk application state is internally inconsistent.
|
||||||
pub(crate) use self::errors::ERROR_CODE_APP_STATE_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_APP_STATE_INVALID;
|
||||||
|
/// Shared Config Desk runtime state cannot be locked safely.
|
||||||
pub(crate) use self::errors::ERROR_CODE_APP_STATE_LOCK_FAILED;
|
pub(crate) use self::errors::ERROR_CODE_APP_STATE_LOCK_FAILED;
|
||||||
|
/// A Documents command requested a registered file that is not a Config-kind document.
|
||||||
pub(crate) use self::errors::ERROR_CODE_DOCUMENT_KIND_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_DOCUMENT_KIND_INVALID;
|
||||||
|
/// Frontend logging requested an unsupported level.
|
||||||
pub(crate) use self::errors::ERROR_CODE_FRONTEND_LOG_LEVEL_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_FRONTEND_LOG_LEVEL_INVALID;
|
||||||
|
/// Frontend logging requested a target outside the application whitelist.
|
||||||
pub(crate) use self::errors::ERROR_CODE_FRONTEND_LOG_TARGET_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_FRONTEND_LOG_TARGET_INVALID;
|
||||||
|
/// Config Desk could not install the managed Logging runtime or its safe fallback.
|
||||||
pub(crate) use self::errors::ERROR_CODE_LOGGING_BOOTSTRAP_FAILED;
|
pub(crate) use self::errors::ERROR_CODE_LOGGING_BOOTSTRAP_FAILED;
|
||||||
|
/// Config Desk persisted a Logging candidate but could not restore the previous source after runtime application failed.
|
||||||
pub(crate) use self::errors::ERROR_CODE_LOGGING_SOURCE_ROLLBACK_FAILED;
|
pub(crate) use self::errors::ERROR_CODE_LOGGING_SOURCE_ROLLBACK_FAILED;
|
||||||
|
/// Controlled Logging test request contains an unsupported or invalid selector.
|
||||||
pub(crate) use self::errors::ERROR_CODE_LOGGING_TEST_REQUEST_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_LOGGING_TEST_REQUEST_INVALID;
|
||||||
|
/// A validated Config document does not expose the standard profile contract expected by the Profiles panel.
|
||||||
pub(crate) use self::errors::ERROR_CODE_PROFILE_CONTRACT_MISSING;
|
pub(crate) use self::errors::ERROR_CODE_PROFILE_CONTRACT_MISSING;
|
||||||
|
/// Config profile data could not be projected safely for the frontend.
|
||||||
pub(crate) use self::errors::ERROR_CODE_PROFILE_PROJECTION_FAILED;
|
pub(crate) use self::errors::ERROR_CODE_PROFILE_PROJECTION_FAILED;
|
||||||
|
/// Privileged reveal was requested for a non-Secret KSP/KSPB namespace.
|
||||||
pub(crate) use self::errors::ERROR_CODE_SECRET_REVEAL_REQUIRES_SECRET;
|
pub(crate) use self::errors::ERROR_CODE_SECRET_REVEAL_REQUIRES_SECRET;
|
||||||
|
/// Privileged reveal requested an unsupported source selector.
|
||||||
pub(crate) use self::errors::ERROR_CODE_SECRET_REVEAL_SOURCE_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_SECRET_REVEAL_SOURCE_INVALID;
|
||||||
|
/// Splash readiness was invoked from a window other than the splash window.
|
||||||
pub(crate) use self::errors::ERROR_CODE_SPLASH_ORIGIN_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_SPLASH_ORIGIN_INVALID;
|
||||||
|
/// A KSP desk splash environment duration is malformed or exceeds its safety bound.
|
||||||
pub(crate) use self::errors::ERROR_CODE_SPLASH_SETTING_INVALID;
|
pub(crate) use self::errors::ERROR_CODE_SPLASH_SETTING_INVALID;
|
||||||
|
/// Tauri runtime assembly or execution failed.
|
||||||
pub(crate) use self::errors::ERROR_CODE_TAURI_RUNTIME_FAILED;
|
pub(crate) use self::errors::ERROR_CODE_TAURI_RUNTIME_FAILED;
|
||||||
|
/// A required Tauri window is missing from the configured application runtime.
|
||||||
pub(crate) use self::errors::ERROR_CODE_TAURI_WINDOW_MISSING;
|
pub(crate) use self::errors::ERROR_CODE_TAURI_WINDOW_MISSING;
|
||||||
|
/// A Tauri window show/focus/destroy/event operation failed.
|
||||||
pub(crate) use self::errors::ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED;
|
pub(crate) use self::errors::ERROR_CODE_TAURI_WINDOW_OPERATION_FAILED;
|
||||||
|
/// Log payload sent by Config Desk frontend scripts.
|
||||||
pub(crate) use self::frontend_logging::FrontendLogPayloadDto;
|
pub(crate) use self::frontend_logging::FrontendLogPayloadDto;
|
||||||
|
/// Emits one validated frontend event through the KSP Logging facade.
|
||||||
pub(crate) use self::frontend_logging::emit_frontend_log_event;
|
pub(crate) use self::frontend_logging::emit_frontend_log_event;
|
||||||
|
/// Console sink configuration exposed to the editor.
|
||||||
|
pub(crate) use self::logging_editor::LoggingConsoleDto;
|
||||||
|
/// Complete editable Logging candidate accepted from the frontend.
|
||||||
pub(crate) use self::logging_editor::LoggingDocumentCandidateDto;
|
pub(crate) use self::logging_editor::LoggingDocumentCandidateDto;
|
||||||
|
/// Complete typed Logging document exposed to the editor.
|
||||||
pub(crate) use self::logging_editor::LoggingDocumentDto;
|
pub(crate) use self::logging_editor::LoggingDocumentDto;
|
||||||
|
/// Result of one validated typed Logging persistence operation.
|
||||||
pub(crate) use self::logging_editor::LoggingDocumentSaveResultDto;
|
pub(crate) use self::logging_editor::LoggingDocumentSaveResultDto;
|
||||||
pub(crate) use self::logging_runtime::{LoggingRuntimeFileDto, LoggingRuntimeStatusDto};
|
/// One persistent file sink exposed to the editor.
|
||||||
pub(crate) use self::logging_test::{LoggingTestRequestDto, LoggingTestResultDto};
|
pub(crate) use self::logging_editor::LoggingFileDto;
|
||||||
|
/// One Logging sink selector/filter exposed to the editor.
|
||||||
|
pub(crate) use self::logging_editor::LoggingOutputFilterDto;
|
||||||
|
/// One typed Logging profile exposed to the editor.
|
||||||
|
pub(crate) use self::logging_editor::LoggingProfileDto;
|
||||||
|
/// One global Logging target override exposed to the editor.
|
||||||
|
pub(crate) use self::logging_editor::LoggingTargetFilterDto;
|
||||||
|
/// Loads the typed Logging source through ConfigManagement and projects it for the frontend.
|
||||||
|
pub(crate) use self::logging_editor::logging_document;
|
||||||
|
/// Validates, atomically persists and hot-reloads one typed Logging candidate through ConfigManagement and `ksp-logging-lib`.
|
||||||
|
pub(crate) use self::logging_editor::save_logging_document;
|
||||||
|
/// Metadata for one currently active persistent Logging file output.
|
||||||
|
pub(crate) use self::logging_runtime::LoggingRuntimeFileDto;
|
||||||
|
/// Safe observable state of the currently active KSP Logging runtime.
|
||||||
|
pub(crate) use self::logging_runtime::LoggingRuntimeStatusDto;
|
||||||
|
/// Applies one persisted Logging profile explicitly without changing `default_profile` or the source document.
|
||||||
|
pub(crate) use self::logging_runtime::apply_logging_profile;
|
||||||
|
/// Executes the crate-internal count to u64 operation for the owning module.
|
||||||
|
pub(crate) use self::logging_runtime::count_to_u64;
|
||||||
|
/// Creates the stable runtime identity for this Config Desk process launch.
|
||||||
|
pub(crate) use self::logging_runtime::launch_identity;
|
||||||
|
/// Returns safe metadata for the currently active Logging runtime.
|
||||||
|
pub(crate) use self::logging_runtime::logging_runtime_status;
|
||||||
|
/// Executes the crate-internal project logging file operation for the owning module.
|
||||||
|
pub(crate) use self::logging_runtime::project_logging_file;
|
||||||
|
/// Controlled request emitted through the backend KSP Logging facade.
|
||||||
|
pub(crate) use self::logging_test::LoggingTestRequestDto;
|
||||||
|
/// Safe result metadata for one controlled backend Logging test request.
|
||||||
|
pub(crate) use self::logging_test::LoggingTestResultDto;
|
||||||
|
/// Emits one controlled backend test request through `ksp-logging-lib` only.
|
||||||
|
pub(crate) use self::logging_test::emit_logging_test;
|
||||||
|
/// Safe detailed view of one resolved Config profile.
|
||||||
pub(crate) use self::profiles::ConfigProfileDetailDto;
|
pub(crate) use self::profiles::ConfigProfileDetailDto;
|
||||||
|
/// One Config document that exposes the standard `default_profile` / `profiles` contract.
|
||||||
pub(crate) use self::profiles::ConfigProfileDocumentDto;
|
pub(crate) use self::profiles::ConfigProfileDocumentDto;
|
||||||
|
/// Resolves one default or explicitly selected profile into a safe inspection DTO.
|
||||||
|
pub(crate) use self::profiles::profile_detail;
|
||||||
|
/// Lists validated Config documents that expose standard profiles.
|
||||||
|
pub(crate) use self::profiles::profile_inventory;
|
||||||
|
/// Privileged reveal request kept separate from ordinary environment-report DTOs.
|
||||||
pub(crate) use self::secrets::SecretRevealRequestDto;
|
pub(crate) use self::secrets::SecretRevealRequestDto;
|
||||||
|
/// Privileged reveal response. This type deliberately does not derive `Clone` or `Debug`.
|
||||||
pub(crate) use self::secrets::SecretRevealResponseDto;
|
pub(crate) use self::secrets::SecretRevealResponseDto;
|
||||||
|
/// Reveals one real Secret value only after the dedicated Tauri command has been explicitly invoked.
|
||||||
|
pub(crate) use self::secrets::reveal_secret;
|
||||||
|
/// Command emitted by Rust to the splash frontend.
|
||||||
pub(crate) use self::splash::SplashOrderDto;
|
pub(crate) use self::splash::SplashOrderDto;
|
||||||
|
/// Runtime timings used by the common desk splash lifecycle.
|
||||||
pub(crate) use self::splash::SplashSettings;
|
pub(crate) use self::splash::SplashSettings;
|
||||||
pub(crate) use self::tw_main::show_and_focus as show_main_window;
|
/// Resolves the required main window or returns a typed error.
|
||||||
pub(crate) use self::tw_splash::frontend_ready as splash_frontend_ready_service;
|
pub(crate) use self::tw_main::require_main_window;
|
||||||
|
/// Shows main window.
|
||||||
|
pub(crate) use self::tw_main::show_main_window;
|
||||||
|
/// Resolves the required splash window or returns a typed error.
|
||||||
|
pub(crate) use self::tw_splash::require_splash_window;
|
||||||
|
/// Executes the crate-internal splash frontend ready service operation for the owning module.
|
||||||
|
pub(crate) use self::tw_splash::splash_frontend_ready_service;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/logging_editor.rs
|
// file: crates/ksp-app-config-desk/src/logging_editor.rs
|
||||||
// version: 4
|
// version: 6
|
||||||
|
|
||||||
//! Typed Logging document projection and validated persistence for the Config Desk Logging editor.
|
//! Typed Logging document projection and validated persistence for the Config Desk Logging editor.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// One Logging sink selector/filter exposed to the editor.
|
/// One Logging sink selector/filter exposed to the editor.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize, TS)]
|
||||||
@@ -32,7 +32,7 @@ pub(crate) struct LoggingConsoleDto {
|
|||||||
/// Console format source text.
|
/// Console format source text.
|
||||||
pub(crate) format: String,
|
pub(crate) format: String,
|
||||||
/// Sink-local selectors.
|
/// Sink-local selectors.
|
||||||
pub(crate) filter: LoggingOutputFilterDto,
|
pub(crate) filter: crate::LoggingOutputFilterDto,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One persistent file sink exposed to the editor.
|
/// One persistent file sink exposed to the editor.
|
||||||
@@ -53,7 +53,7 @@ pub(crate) struct LoggingFileDto {
|
|||||||
/// Whether ANSI output is requested.
|
/// Whether ANSI output is requested.
|
||||||
pub(crate) ansi: bool,
|
pub(crate) ansi: bool,
|
||||||
/// Sink-local selectors.
|
/// Sink-local selectors.
|
||||||
pub(crate) filter: LoggingOutputFilterDto,
|
pub(crate) filter: crate::LoggingOutputFilterDto,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One global Logging target override exposed to the editor.
|
/// One global Logging target override exposed to the editor.
|
||||||
@@ -79,11 +79,11 @@ pub(crate) struct LoggingProfileDto {
|
|||||||
/// Span lifecycle source text.
|
/// Span lifecycle source text.
|
||||||
pub(crate) span_events: String,
|
pub(crate) span_events: String,
|
||||||
/// Console sink configuration.
|
/// Console sink configuration.
|
||||||
pub(crate) console: LoggingConsoleDto,
|
pub(crate) console: crate::LoggingConsoleDto,
|
||||||
/// Persistent file sinks in source order.
|
/// Persistent file sinks in source order.
|
||||||
pub(crate) files: std::vec::Vec<LoggingFileDto>,
|
pub(crate) files: std::vec::Vec<crate::LoggingFileDto>,
|
||||||
/// Global target overrides in source order.
|
/// Global target overrides in source order.
|
||||||
pub(crate) target_filters: std::vec::Vec<LoggingTargetFilterDto>,
|
pub(crate) target_filters: std::vec::Vec<crate::LoggingTargetFilterDto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete typed Logging document exposed to the editor.
|
/// Complete typed Logging document exposed to the editor.
|
||||||
@@ -102,7 +102,7 @@ pub(crate) struct LoggingDocumentDto {
|
|||||||
/// Autonomous default profile identifier.
|
/// Autonomous default profile identifier.
|
||||||
pub(crate) default_profile: String,
|
pub(crate) default_profile: String,
|
||||||
/// Typed profiles in source order.
|
/// Typed profiles in source order.
|
||||||
pub(crate) profiles: std::vec::Vec<LoggingProfileDto>,
|
pub(crate) profiles: std::vec::Vec<crate::LoggingProfileDto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Complete editable Logging candidate accepted from the frontend.
|
/// Complete editable Logging candidate accepted from the frontend.
|
||||||
@@ -115,7 +115,7 @@ pub(crate) struct LoggingDocumentCandidateDto {
|
|||||||
/// Autonomous default profile identifier.
|
/// Autonomous default profile identifier.
|
||||||
pub(crate) default_profile: String,
|
pub(crate) default_profile: String,
|
||||||
/// Typed profiles in source order.
|
/// Typed profiles in source order.
|
||||||
pub(crate) profiles: std::vec::Vec<LoggingProfileDto>,
|
pub(crate) profiles: std::vec::Vec<crate::LoggingProfileDto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of one validated typed Logging persistence operation.
|
/// Result of one validated typed Logging persistence operation.
|
||||||
@@ -138,12 +138,12 @@ pub(crate) struct LoggingDocumentSaveResultDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Loads the typed Logging source through ConfigManagement and projects it for the frontend.
|
/// Loads the typed Logging source through ConfigManagement and projects it for the frontend.
|
||||||
pub(crate) fn document(state: &crate::AppState) -> ksp_core_lib::Result<LoggingDocumentDto> {
|
pub(crate) fn logging_document(state: &crate::AppState) -> ksp_core_lib::Result<LoggingDocumentDto> {
|
||||||
return document_from_management(state.config_management());
|
return document_from_management(state.config_management());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Validates, atomically persists and hot-reloads one typed Logging candidate through ConfigManagement and `ksp-logging-lib`.
|
/// Validates, atomically persists and hot-reloads one typed Logging candidate through ConfigManagement and `ksp-logging-lib`.
|
||||||
pub(crate) fn save(state: &crate::AppState, candidate: LoggingDocumentCandidateDto) -> ksp_core_lib::Result<LoggingDocumentSaveResultDto> {
|
pub(crate) fn save_logging_document(state: &crate::AppState, candidate: LoggingDocumentCandidateDto) -> ksp_core_lib::Result<LoggingDocumentSaveResultDto> {
|
||||||
let file_id = ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_STD_LOGGING);
|
let file_id = ksp_config_lib::ConfigFileId::new(ksp_config_lib::FILE_ID_STD_LOGGING);
|
||||||
let file_id = match file_id {
|
let file_id = match file_id {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -255,7 +255,7 @@ fn document_from_management(management: &ksp_config_lib::ConfigManagement) -> ks
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let mut profiles = std::vec::Vec::<LoggingProfileDto>::with_capacity(source.profiles().len());
|
let mut profiles = std::vec::Vec::<crate::LoggingProfileDto>::with_capacity(source.profiles().len());
|
||||||
let mut file_count = 0usize;
|
let mut file_count = 0usize;
|
||||||
let mut target_filter_count = 0usize;
|
let mut target_filter_count = 0usize;
|
||||||
for profile in source.profiles() {
|
for profile in source.profiles() {
|
||||||
@@ -291,7 +291,7 @@ fn config_candidate(candidate: LoggingDocumentCandidateDto) -> ksp_config_lib::L
|
|||||||
return ksp_config_lib::LoggingConfigDocument::new(candidate.logs_directory, candidate.default_profile, profiles);
|
return ksp_config_lib::LoggingConfigDocument::new(candidate.logs_directory, candidate.default_profile, profiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config_profile(profile: LoggingProfileDto) -> ksp_config_lib::LoggingProfileConfig {
|
fn config_profile(profile: crate::LoggingProfileDto) -> ksp_config_lib::LoggingProfileConfig {
|
||||||
let mut files = std::vec::Vec::<ksp_config_lib::LoggingFileConfig>::with_capacity(profile.files.len());
|
let mut files = std::vec::Vec::<ksp_config_lib::LoggingFileConfig>::with_capacity(profile.files.len());
|
||||||
for file in profile.files {
|
for file in profile.files {
|
||||||
let mut config =
|
let mut config =
|
||||||
@@ -319,14 +319,14 @@ fn config_profile(profile: LoggingProfileDto) -> ksp_config_lib::LoggingProfileC
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn config_output_filter(filter: LoggingOutputFilterDto) -> ksp_config_lib::LoggingOutputFilterConfig {
|
fn config_output_filter(filter: crate::LoggingOutputFilterDto) -> ksp_config_lib::LoggingOutputFilterConfig {
|
||||||
return ksp_config_lib::LoggingOutputFilterConfig::new(filter.level, filter.targets, filter.domains);
|
return ksp_config_lib::LoggingOutputFilterConfig::new(filter.level, filter.targets, filter.domains);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_profile(profile: &ksp_config_lib::LoggingProfileConfig) -> LoggingProfileDto {
|
fn project_profile(profile: &ksp_config_lib::LoggingProfileConfig) -> crate::LoggingProfileDto {
|
||||||
let mut files = std::vec::Vec::<LoggingFileDto>::with_capacity(profile.files().len());
|
let mut files = std::vec::Vec::<crate::LoggingFileDto>::with_capacity(profile.files().len());
|
||||||
for file in profile.files() {
|
for file in profile.files() {
|
||||||
files.push(LoggingFileDto {
|
files.push(crate::LoggingFileDto {
|
||||||
output_id: file.output_id().to_owned(),
|
output_id: file.output_id().to_owned(),
|
||||||
enabled: file.enabled(),
|
enabled: file.enabled(),
|
||||||
path: file.path().to_owned(),
|
path: file.path().to_owned(),
|
||||||
@@ -336,15 +336,15 @@ fn project_profile(profile: &ksp_config_lib::LoggingProfileConfig) -> LoggingPro
|
|||||||
filter: project_output_filter(file.filter()),
|
filter: project_output_filter(file.filter()),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
let mut target_filters = std::vec::Vec::<LoggingTargetFilterDto>::with_capacity(profile.target_filters().len());
|
let mut target_filters = std::vec::Vec::<crate::LoggingTargetFilterDto>::with_capacity(profile.target_filters().len());
|
||||||
for target_filter in profile.target_filters() {
|
for target_filter in profile.target_filters() {
|
||||||
target_filters.push(LoggingTargetFilterDto { target_prefix: target_filter.target_prefix().to_owned(), level: target_filter.level().to_owned() });
|
target_filters.push(crate::LoggingTargetFilterDto { target_prefix: target_filter.target_prefix().to_owned(), level: target_filter.level().to_owned() });
|
||||||
}
|
}
|
||||||
return LoggingProfileDto {
|
return crate::LoggingProfileDto {
|
||||||
profile_id: profile.profile_id().to_owned(),
|
profile_id: profile.profile_id().to_owned(),
|
||||||
default_filter: profile.default_filter().to_owned(),
|
default_filter: profile.default_filter().to_owned(),
|
||||||
span_events: profile.span_events().to_owned(),
|
span_events: profile.span_events().to_owned(),
|
||||||
console: LoggingConsoleDto {
|
console: crate::LoggingConsoleDto {
|
||||||
enabled: profile.console().enabled(),
|
enabled: profile.console().enabled(),
|
||||||
output: profile.console().output().to_owned(),
|
output: profile.console().output().to_owned(),
|
||||||
ansi: profile.console().ansi(),
|
ansi: profile.console().ansi(),
|
||||||
@@ -356,8 +356,8 @@ fn project_profile(profile: &ksp_config_lib::LoggingProfileConfig) -> LoggingPro
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn project_output_filter(filter: &ksp_config_lib::LoggingOutputFilterConfig) -> LoggingOutputFilterDto {
|
fn project_output_filter(filter: &ksp_config_lib::LoggingOutputFilterConfig) -> crate::LoggingOutputFilterDto {
|
||||||
return LoggingOutputFilterDto {
|
return crate::LoggingOutputFilterDto {
|
||||||
level: filter.level().to_owned(),
|
level: filter.level().to_owned(),
|
||||||
targets: filter.targets().to_vec(),
|
targets: filter.targets().to_vec(),
|
||||||
domains: filter.domains().to_vec(),
|
domains: filter.domains().to_vec(),
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/logging_runtime.rs
|
// file: crates/ksp-app-config-desk/src/logging_runtime.rs
|
||||||
// version: 2
|
// version: 4
|
||||||
|
|
||||||
//! Runtime Logging metadata, launch identity and explicit profile application for Config Desk.
|
//! Runtime Logging metadata, launch identity and explicit profile application for Config Desk.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// Metadata for one currently active persistent Logging file output.
|
/// Metadata for one currently active persistent Logging file output.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
@@ -56,12 +56,12 @@ pub(crate) fn launch_identity() -> ksp_core_lib::Result<ksp_logging_lib::Logging
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns safe metadata for the currently active Logging runtime.
|
/// Returns safe metadata for the currently active Logging runtime.
|
||||||
pub(crate) fn status(state: &crate::AppState) -> ksp_core_lib::Result<LoggingRuntimeStatusDto> {
|
pub(crate) fn logging_runtime_status(state: &crate::AppState) -> ksp_core_lib::Result<LoggingRuntimeStatusDto> {
|
||||||
return state.logging_runtime_status();
|
return state.logging_runtime_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Applies one persisted Logging profile explicitly without changing `default_profile` or the source document.
|
/// Applies one persisted Logging profile explicitly without changing `default_profile` or the source document.
|
||||||
pub(crate) fn apply_profile(state: &crate::AppState, profile_id: &str) -> ksp_core_lib::Result<LoggingRuntimeStatusDto> {
|
pub(crate) fn apply_logging_profile(state: &crate::AppState, profile_id: &str) -> ksp_core_lib::Result<LoggingRuntimeStatusDto> {
|
||||||
let environment = ksp_config_lib::ConfigEnvironment::load();
|
let environment = ksp_config_lib::ConfigEnvironment::load();
|
||||||
let environment = match environment {
|
let environment = match environment {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -88,7 +88,8 @@ pub(crate) fn apply_profile(state: &crate::AppState, profile_id: &str) -> ksp_co
|
|||||||
return state.logging_runtime_status();
|
return state.logging_runtime_status();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn project_file(metadata: &ksp_logging_lib::RuntimeFileMetadata) -> LoggingRuntimeFileDto {
|
/// Executes the crate-internal project logging file operation for the owning module.
|
||||||
|
pub(crate) fn project_logging_file(metadata: &ksp_logging_lib::RuntimeFileMetadata) -> LoggingRuntimeFileDto {
|
||||||
return LoggingRuntimeFileDto {
|
return LoggingRuntimeFileDto {
|
||||||
output_id: metadata.output_id().to_owned(),
|
output_id: metadata.output_id().to_owned(),
|
||||||
directory: metadata.directory().display().to_string(),
|
directory: metadata.directory().display().to_string(),
|
||||||
@@ -97,7 +98,7 @@ pub(crate) fn project_file(metadata: &ksp_logging_lib::RuntimeFileMetadata) -> L
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) const fn rotation_label(rotation: ksp_logging_lib::FileRotation) -> &'static str {
|
const fn rotation_label(rotation: ksp_logging_lib::FileRotation) -> &'static str {
|
||||||
return match rotation {
|
return match rotation {
|
||||||
ksp_logging_lib::FileRotation::Never => "never",
|
ksp_logging_lib::FileRotation::Never => "never",
|
||||||
ksp_logging_lib::FileRotation::Hourly => "hourly",
|
ksp_logging_lib::FileRotation::Hourly => "hourly",
|
||||||
@@ -105,6 +106,7 @@ pub(crate) const fn rotation_label(rotation: ksp_logging_lib::FileRotation) -> &
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the crate-internal count to u64 operation for the owning module.
|
||||||
pub(crate) fn count_to_u64(value: usize) -> u64 {
|
pub(crate) fn count_to_u64(value: usize) -> u64 {
|
||||||
let converted = u64::try_from(value);
|
let converted = u64::try_from(value);
|
||||||
return match converted {
|
return match converted {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/logging_test.rs
|
// file: crates/ksp-app-config-desk/src/logging_test.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Controlled Logging test events for validating KSP runtime routing from Config Desk.
|
//! Controlled Logging test events for validating KSP runtime routing from Config Desk.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// Controlled request emitted through the backend KSP Logging facade.
|
/// Controlled request emitted through the backend KSP Logging facade.
|
||||||
#[derive(Clone, Debug, serde::Deserialize, TS)]
|
#[derive(Clone, Debug, serde::Deserialize, TS)]
|
||||||
@@ -56,7 +56,7 @@ enum LoggingTestTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Emits one controlled backend test request through `ksp-logging-lib` only.
|
/// Emits one controlled backend test request through `ksp-logging-lib` only.
|
||||||
pub(crate) fn emit(state: &crate::AppState, request: LoggingTestRequestDto) -> ksp_core_lib::Result<LoggingTestResultDto> {
|
pub(crate) fn emit_logging_test(state: &crate::AppState, request: LoggingTestRequestDto) -> ksp_core_lib::Result<LoggingTestResultDto> {
|
||||||
let level = parse_level(request.level.as_str());
|
let level = parse_level(request.level.as_str());
|
||||||
let level = match level {
|
let level = match level {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/profiles.rs
|
// file: crates/ksp-app-config-desk/src/profiles.rs
|
||||||
// version: 1
|
// version: 4
|
||||||
|
|
||||||
//! Safe Config profile inspection and provenance projections for Config Desk.
|
//! Safe Config profile inspection and provenance projections for Config Desk.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// One Config document that exposes the standard `default_profile` / `profiles` contract.
|
/// One Config document that exposes the standard `default_profile` / `profiles` contract.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
@@ -77,13 +77,22 @@ pub(crate) struct ConfigProfileDetailDto {
|
|||||||
pub(crate) environment_provenance: std::vec::Vec<ConfigProfileEnvironmentProvenanceDto>,
|
pub(crate) environment_provenance: std::vec::Vec<ConfigProfileEnvironmentProvenanceDto>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ProfileContract {
|
||||||
|
default_profile: String,
|
||||||
|
profile_ids: std::vec::Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Lists validated Config documents that expose standard profiles.
|
/// Lists validated Config documents that expose standard profiles.
|
||||||
pub(crate) fn inventory(state: &crate::AppState) -> ksp_core_lib::Result<std::vec::Vec<ConfigProfileDocumentDto>> {
|
pub(crate) fn profile_inventory(state: &crate::AppState) -> ksp_core_lib::Result<std::vec::Vec<ConfigProfileDocumentDto>> {
|
||||||
return inventory_from_management(state.config_management());
|
return inventory_from_management(state.config_management());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves one default or explicitly selected profile into a safe inspection DTO.
|
/// Resolves one default or explicitly selected profile into a safe inspection DTO.
|
||||||
pub(crate) fn detail(state: &crate::AppState, file_id: &str, requested_profile: std::option::Option<&str>) -> ksp_core_lib::Result<ConfigProfileDetailDto> {
|
pub(crate) fn profile_detail(
|
||||||
|
state: &crate::AppState,
|
||||||
|
file_id: &str,
|
||||||
|
requested_profile: std::option::Option<&str>,
|
||||||
|
) -> ksp_core_lib::Result<ConfigProfileDetailDto> {
|
||||||
return detail_from_management(state.config_management(), file_id, requested_profile);
|
return detail_from_management(state.config_management(), file_id, requested_profile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -213,11 +222,6 @@ fn detail_from_management(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ProfileContract {
|
|
||||||
default_profile: String,
|
|
||||||
profile_ids: std::vec::Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn profile_contract(document: &ksp_config_lib::ConfigJsonDocument) -> ksp_core_lib::Result<std::option::Option<ProfileContract>> {
|
fn profile_contract(document: &ksp_config_lib::ConfigJsonDocument) -> ksp_core_lib::Result<std::option::Option<ProfileContract>> {
|
||||||
let root = match document.value().as_object() {
|
let root = match document.value().as_object() {
|
||||||
std::option::Option::Some(value) => value,
|
std::option::Option::Some(value) => value,
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/secrets.rs
|
// file: crates/ksp-app-config-desk/src/secrets.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Privileged, explicitly requested Config Secret reveal boundary for Config Desk.
|
//! Privileged, explicitly requested Config Secret reveal boundary for Config Desk.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
/// Privileged reveal request kept separate from ordinary environment-report DTOs.
|
/// Privileged reveal request kept separate from ordinary environment-report DTOs.
|
||||||
#[derive(serde::Deserialize, TS)]
|
#[derive(serde::Deserialize, TS)]
|
||||||
@@ -56,7 +56,7 @@ impl SecretRevealSource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Reveals one real Secret value only after the dedicated Tauri command has been explicitly invoked.
|
/// Reveals one real Secret value only after the dedicated Tauri command has been explicitly invoked.
|
||||||
pub(crate) fn reveal(state: &crate::AppState, request: SecretRevealRequestDto) -> ksp_core_lib::Result<SecretRevealResponseDto> {
|
pub(crate) fn reveal_secret(state: &crate::AppState, request: SecretRevealRequestDto) -> ksp_core_lib::Result<SecretRevealResponseDto> {
|
||||||
return reveal_from_management(state.config_management(), request);
|
return reveal_from_management(state.config_management(), request);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/splash.rs
|
// file: crates/ksp-app-config-desk/src/splash.rs
|
||||||
// version: 3
|
// version: 5
|
||||||
|
|
||||||
//! Common splash settings and frontend event contracts for Config Desk.
|
//! Common splash settings and frontend event contracts for Config Desk.
|
||||||
|
|
||||||
use ts_rs::TS; // rust-rules: derive-import
|
use ts_rs::TS; // rust-rules: trait-import
|
||||||
|
|
||||||
const ENV_SPLASH_MINIMUM_MS: &str = "KSP_DESK_SPLASH_MINIMUM_MS";
|
|
||||||
const ENV_SPLASH_FADE_MS: &str = "KSP_DESK_SPLASH_FADE_MS";
|
|
||||||
const DEFAULT_SPLASH_MINIMUM_MS: u64 = 1200;
|
|
||||||
const DEFAULT_SPLASH_FADE_MS: u32 = 300;
|
const DEFAULT_SPLASH_FADE_MS: u32 = 300;
|
||||||
const MAX_SPLASH_MINIMUM_MS: u64 = 60_000;
|
const DEFAULT_SPLASH_MINIMUM_MS: u64 = 1200;
|
||||||
|
const ENV_SPLASH_FADE_MS: &str = "KSP_DESK_SPLASH_FADE_MS";
|
||||||
|
const ENV_SPLASH_MINIMUM_MS: &str = "KSP_DESK_SPLASH_MINIMUM_MS";
|
||||||
const MAX_SPLASH_FADE_MS: u32 = 10_000;
|
const MAX_SPLASH_FADE_MS: u32 = 10_000;
|
||||||
|
const MAX_SPLASH_MINIMUM_MS: u64 = 60_000;
|
||||||
|
|
||||||
/// Runtime timings used by the common desk splash lifecycle.
|
/// Runtime timings used by the common desk splash lifecycle.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
@@ -94,14 +94,6 @@ impl SplashSettings {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const fn environment_source_code(source: ksp_config_lib::ConfigEnvironmentSource) -> &'static str {
|
|
||||||
return match source {
|
|
||||||
ksp_config_lib::ConfigEnvironmentSource::Process => "process",
|
|
||||||
ksp_config_lib::ConfigEnvironmentSource::DotEnv => "dotenv",
|
|
||||||
ksp_config_lib::ConfigEnvironmentSource::Fallback => "fallback",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Command emitted by Rust to the splash frontend.
|
/// Command emitted by Rust to the splash frontend.
|
||||||
#[derive(Clone, Debug, serde::Serialize, TS)]
|
#[derive(Clone, Debug, serde::Serialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
@@ -116,12 +108,21 @@ pub(crate) struct SplashOrderDto {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SplashOrderDto {
|
impl SplashOrderDto {
|
||||||
|
/// Creates a new `SplashOrderDto` value.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub(crate) fn new(action: &str, message: std::option::Option<&str>, duration_ms: std::option::Option<u32>) -> Self {
|
pub(crate) fn new(action: &str, message: std::option::Option<&str>, duration_ms: std::option::Option<u32>) -> Self {
|
||||||
return Self { action: action.to_owned(), message: message.map(std::string::ToString::to_string), duration_ms };
|
return Self { action: action.to_owned(), message: message.map(std::string::ToString::to_string), duration_ms };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn environment_source_code(source: ksp_config_lib::ConfigEnvironmentSource) -> &'static str {
|
||||||
|
return match source {
|
||||||
|
ksp_config_lib::ConfigEnvironmentSource::Process => "process",
|
||||||
|
ksp_config_lib::ConfigEnvironmentSource::DotEnv => "dotenv",
|
||||||
|
ksp_config_lib::ConfigEnvironmentSource::Fallback => "fallback",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_u64_setting(variable_name: &str, value: &str, maximum: u64) -> ksp_core_lib::Result<u64> {
|
fn parse_u64_setting(variable_name: &str, value: &str, maximum: u64) -> ksp_core_lib::Result<u64> {
|
||||||
let parsed = value.parse::<u64>();
|
let parsed = value.parse::<u64>();
|
||||||
let parsed = match parsed {
|
let parsed = match parsed {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/tauri.rs
|
// file: crates/ksp-app-config-desk/src/tauri.rs
|
||||||
// version: 15
|
// version: 16
|
||||||
|
|
||||||
//! Tauri runtime assembly for the KSP configuration desktop application.
|
//! Tauri runtime assembly for the KSP configuration desktop application.
|
||||||
|
|
||||||
@@ -60,11 +60,11 @@ fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tau
|
|||||||
|
|
||||||
fn configure_setup(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
|
fn configure_setup(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
|
||||||
return builder.setup(|app| {
|
return builder.setup(|app| {
|
||||||
let splash = crate::tw_splash::require_window(app);
|
let splash = crate::require_splash_window(app);
|
||||||
if let std::result::Result::Err(error) = splash {
|
if let std::result::Result::Err(error) = splash {
|
||||||
return std::result::Result::Err(std::boxed::Box::new(error));
|
return std::result::Result::Err(std::boxed::Box::new(error));
|
||||||
}
|
}
|
||||||
let main = crate::tw_main::require_window(app);
|
let main = crate::require_main_window(app);
|
||||||
if let std::result::Result::Err(error) = main {
|
if let std::result::Result::Err(error) = main {
|
||||||
return std::result::Result::Err(std::boxed::Box::new(error));
|
return std::result::Result::Err(std::boxed::Box::new(error));
|
||||||
}
|
}
|
||||||
@@ -83,7 +83,7 @@ fn get_app_snapshot(state: tauri::State<'_, crate::AppState>) -> std::result::Re
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_config_documents(state: tauri::State<'_, crate::AppState>) -> std::vec::Vec<crate::ConfigDocumentSummaryDto> {
|
fn get_config_documents(state: tauri::State<'_, crate::AppState>) -> std::vec::Vec<crate::ConfigDocumentSummaryDto> {
|
||||||
return crate::documents::inventory(&state);
|
return crate::document_inventory(&state);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -91,7 +91,7 @@ fn get_config_document_detail(
|
|||||||
file_id: String,
|
file_id: String,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::ConfigDocumentDetailDto, crate::ConfigDocumentErrorDto> {
|
) -> std::result::Result<crate::ConfigDocumentDetailDto, crate::ConfigDocumentErrorDto> {
|
||||||
return crate::documents::detail(&state, file_id.as_str());
|
return crate::document_detail(&state, file_id.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -100,14 +100,14 @@ fn save_config_document_source(
|
|||||||
source: String,
|
source: String,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::ConfigDocumentSaveResultDto, crate::ConfigDocumentErrorDto> {
|
) -> std::result::Result<crate::ConfigDocumentSaveResultDto, crate::ConfigDocumentErrorDto> {
|
||||||
return crate::documents::save_source(&state, file_id.as_str(), source.as_str());
|
return crate::save_document_source(&state, file_id.as_str(), source.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_config_profile_documents(
|
fn get_config_profile_documents(
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<std::vec::Vec<crate::ConfigProfileDocumentDto>, crate::CommandErrorDto> {
|
) -> std::result::Result<std::vec::Vec<crate::ConfigProfileDocumentDto>, crate::CommandErrorDto> {
|
||||||
let result = crate::profiles::inventory(&state);
|
let result = crate::profile_inventory(&state);
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -120,7 +120,7 @@ fn get_config_profile_detail(
|
|||||||
profile_id: std::option::Option<String>,
|
profile_id: std::option::Option<String>,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::ConfigProfileDetailDto, crate::CommandErrorDto> {
|
) -> std::result::Result<crate::ConfigProfileDetailDto, crate::CommandErrorDto> {
|
||||||
let result = crate::profiles::detail(&state, file_id.as_str(), profile_id.as_deref());
|
let result = crate::profile_detail(&state, file_id.as_str(), profile_id.as_deref());
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -131,7 +131,7 @@ fn get_config_profile_detail(
|
|||||||
fn get_environment_report(
|
fn get_environment_report(
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<std::vec::Vec<crate::ConfigEnvironmentReportDto>, crate::CommandErrorDto> {
|
) -> std::result::Result<std::vec::Vec<crate::ConfigEnvironmentReportDto>, crate::CommandErrorDto> {
|
||||||
let result = crate::environment::report(&state);
|
let result = crate::environment_report(&state);
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -144,7 +144,7 @@ fn set_environment_value(
|
|||||||
value: String,
|
value: String,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::ConfigEnvironmentChangeDto, crate::CommandErrorDto> {
|
) -> std::result::Result<crate::ConfigEnvironmentChangeDto, crate::CommandErrorDto> {
|
||||||
let result = crate::environment::set_value(&state, variable_name.as_str(), value.as_str());
|
let result = crate::set_environment_value(&state, variable_name.as_str(), value.as_str());
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -156,7 +156,7 @@ fn remove_environment_value(
|
|||||||
variable_name: String,
|
variable_name: String,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::ConfigEnvironmentChangeDto, crate::CommandErrorDto> {
|
) -> std::result::Result<crate::ConfigEnvironmentChangeDto, crate::CommandErrorDto> {
|
||||||
let result = crate::environment::remove_value(&state, variable_name.as_str());
|
let result = crate::remove_environment_value(&state, variable_name.as_str());
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -168,7 +168,7 @@ fn reveal_environment_value(
|
|||||||
request: crate::SecretRevealRequestDto,
|
request: crate::SecretRevealRequestDto,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::SecretRevealResponseDto, crate::CommandErrorDto> {
|
) -> std::result::Result<crate::SecretRevealResponseDto, crate::CommandErrorDto> {
|
||||||
let result = crate::secrets::reveal(&state, request);
|
let result = crate::reveal_secret(&state, request);
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -177,7 +177,7 @@ fn reveal_environment_value(
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_logging_document(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::LoggingDocumentDto, crate::CommandErrorDto> {
|
fn get_logging_document(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::LoggingDocumentDto, crate::CommandErrorDto> {
|
||||||
let result = crate::logging_editor::document(&state);
|
let result = crate::logging_document(&state);
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -186,7 +186,7 @@ fn get_logging_document(state: tauri::State<'_, crate::AppState>) -> std::result
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
fn get_logging_runtime_status(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::LoggingRuntimeStatusDto, crate::CommandErrorDto> {
|
fn get_logging_runtime_status(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::LoggingRuntimeStatusDto, crate::CommandErrorDto> {
|
||||||
let result = crate::logging_runtime::status(&state);
|
let result = crate::logging_runtime_status(&state);
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -198,7 +198,7 @@ fn apply_logging_profile(
|
|||||||
profile_id: String,
|
profile_id: String,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::LoggingRuntimeStatusDto, crate::CommandErrorDto> {
|
) -> std::result::Result<crate::LoggingRuntimeStatusDto, crate::CommandErrorDto> {
|
||||||
let result = crate::logging_runtime::apply_profile(&state, profile_id.as_str());
|
let result = crate::apply_logging_profile(&state, profile_id.as_str());
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -210,7 +210,7 @@ fn save_logging_document(
|
|||||||
candidate: crate::LoggingDocumentCandidateDto,
|
candidate: crate::LoggingDocumentCandidateDto,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::LoggingDocumentSaveResultDto, crate::CommandErrorDto> {
|
) -> std::result::Result<crate::LoggingDocumentSaveResultDto, crate::CommandErrorDto> {
|
||||||
let result = crate::logging_editor::save(&state, candidate);
|
let result = crate::save_logging_document(&state, candidate);
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
@@ -222,7 +222,7 @@ fn emit_logging_test(
|
|||||||
request: crate::LoggingTestRequestDto,
|
request: crate::LoggingTestRequestDto,
|
||||||
state: tauri::State<'_, crate::AppState>,
|
state: tauri::State<'_, crate::AppState>,
|
||||||
) -> std::result::Result<crate::LoggingTestResultDto, crate::CommandErrorDto> {
|
) -> std::result::Result<crate::LoggingTestResultDto, crate::CommandErrorDto> {
|
||||||
let result = crate::logging_test::emit(&state, request);
|
let result = crate::emit_logging_test(&state, request);
|
||||||
return match result {
|
return match result {
|
||||||
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
std::result::Result::Ok(value) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/tw_main.rs
|
// file: crates/ksp-app-config-desk/src/tw_main.rs
|
||||||
// version: 1
|
// version: 4
|
||||||
|
|
||||||
//! Tauri-window helpers for the Config Desk main window.
|
//! Tauri-window helpers for the Config Desk main window.
|
||||||
|
|
||||||
use tauri::Manager; // rust-rules: trait-import
|
use tauri::Manager; // rust-rules: trait-import
|
||||||
|
|
||||||
|
/// Crate-internal `WINDOW_LABEL_MAIN` constant.
|
||||||
pub(crate) const WINDOW_LABEL_MAIN: &str = "main";
|
pub(crate) const WINDOW_LABEL_MAIN: &str = "main";
|
||||||
|
|
||||||
pub(crate) fn require_window(manager: &impl Manager<tauri::Wry>) -> ksp_core_lib::Result<tauri::WebviewWindow> {
|
/// Resolves the required main window or returns a typed error.
|
||||||
|
pub(crate) fn require_main_window(manager: &impl Manager<tauri::Wry>) -> ksp_core_lib::Result<tauri::WebviewWindow> {
|
||||||
let window = manager.get_webview_window(WINDOW_LABEL_MAIN);
|
let window = manager.get_webview_window(WINDOW_LABEL_MAIN);
|
||||||
return match window {
|
return match window {
|
||||||
std::option::Option::Some(value) => std::result::Result::Ok(value),
|
std::option::Option::Some(value) => std::result::Result::Ok(value),
|
||||||
@@ -18,8 +20,9 @@ pub(crate) fn require_window(manager: &impl Manager<tauri::Wry>) -> ksp_core_lib
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn show_and_focus(app: &tauri::AppHandle) -> ksp_core_lib::Result<()> {
|
/// Shows main window.
|
||||||
let window = require_window(app);
|
pub(crate) fn show_main_window(app: &tauri::AppHandle) -> ksp_core_lib::Result<()> {
|
||||||
|
let window = crate::require_main_window(app);
|
||||||
let window = match window {
|
let window = match window {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
|
|||||||
@@ -1,15 +1,18 @@
|
|||||||
// file: crates/ksp-app-config-desk/src/tw_splash.rs
|
// file: crates/ksp-app-config-desk/src/tw_splash.rs
|
||||||
// version: 2
|
// version: 5
|
||||||
|
|
||||||
//! Tauri-window lifecycle for the Config Desk splash window.
|
//! Tauri-window lifecycle for the Config Desk splash window.
|
||||||
|
|
||||||
use tauri::Emitter; // rust-rules: trait-import
|
use tauri::Emitter; // rust-rules: trait-import
|
||||||
use tauri::Manager; // rust-rules: trait-import
|
use tauri::Manager; // rust-rules: trait-import
|
||||||
|
|
||||||
|
/// Crate-internal `WINDOW_LABEL_SPLASH` constant.
|
||||||
pub(crate) const WINDOW_LABEL_SPLASH: &str = "splash";
|
pub(crate) const WINDOW_LABEL_SPLASH: &str = "splash";
|
||||||
|
|
||||||
const SPLASH_EVENT_NAME: &str = "ksp-splash-order";
|
const SPLASH_EVENT_NAME: &str = "ksp-splash-order";
|
||||||
|
|
||||||
pub(crate) fn require_window(manager: &impl Manager<tauri::Wry>) -> ksp_core_lib::Result<tauri::WebviewWindow> {
|
/// Resolves the required splash window or returns a typed error.
|
||||||
|
pub(crate) fn require_splash_window(manager: &impl Manager<tauri::Wry>) -> ksp_core_lib::Result<tauri::WebviewWindow> {
|
||||||
let window = manager.get_webview_window(WINDOW_LABEL_SPLASH);
|
let window = manager.get_webview_window(WINDOW_LABEL_SPLASH);
|
||||||
return match window {
|
return match window {
|
||||||
std::option::Option::Some(value) => std::result::Result::Ok(value),
|
std::option::Option::Some(value) => std::result::Result::Ok(value),
|
||||||
@@ -20,7 +23,12 @@ pub(crate) fn require_window(manager: &impl Manager<tauri::Wry>) -> ksp_core_lib
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn frontend_ready(app: tauri::AppHandle, invoking_window: tauri::WebviewWindow, state: &crate::AppState) -> ksp_core_lib::Result<()> {
|
/// Executes the crate-internal splash frontend ready service operation for the owning module.
|
||||||
|
pub(crate) async fn splash_frontend_ready_service(
|
||||||
|
app: tauri::AppHandle,
|
||||||
|
invoking_window: tauri::WebviewWindow,
|
||||||
|
state: &crate::AppState,
|
||||||
|
) -> ksp_core_lib::Result<()> {
|
||||||
if invoking_window.label() != WINDOW_LABEL_SPLASH {
|
if invoking_window.label() != WINDOW_LABEL_SPLASH {
|
||||||
return std::result::Result::Err(
|
return std::result::Result::Err(
|
||||||
ksp_core_lib::Error::new(crate::ERROR_CODE_SPLASH_ORIGIN_INVALID, "Splash readiness may only originate from the splash window")
|
ksp_core_lib::Error::new(crate::ERROR_CODE_SPLASH_ORIGIN_INVALID, "Splash readiness may only originate from the splash window")
|
||||||
|
|||||||
@@ -1,31 +1,29 @@
|
|||||||
// file: crates/ksp-app-config-desk/unit_tests/documents.rs
|
// file: crates/ksp-app-config-desk/unit_tests/documents.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn diagnostic_classifier_distinguishes_json_schema_semantic_and_effective_errors() {
|
fn diagnostic_classifier_distinguishes_json_schema_semantic_and_effective_errors() {
|
||||||
let json = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_JSON_SYNTAX_INVALID, "json");
|
let json = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_JSON_SYNTAX_INVALID, "json");
|
||||||
assert_eq!(classify_error(&json), STAGE_JSON);
|
assert_eq!(super::classify_error(&json), super::STAGE_JSON);
|
||||||
let schema = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_SCHEMA_VALIDATION_FAILED, "schema");
|
let schema = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_SCHEMA_VALIDATION_FAILED, "schema");
|
||||||
assert_eq!(classify_error(&schema), STAGE_SCHEMA);
|
assert_eq!(super::classify_error(&schema), super::STAGE_SCHEMA);
|
||||||
let semantic = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_DOCUMENT_SEMANTIC_INVALID, "semantic");
|
let semantic = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_DOCUMENT_SEMANTIC_INVALID, "semantic");
|
||||||
assert_eq!(classify_error(&semantic), STAGE_SEMANTIC);
|
assert_eq!(super::classify_error(&semantic), super::STAGE_SEMANTIC);
|
||||||
let effective = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_EFFECTIVE_CONFIG_INVALID, "effective");
|
let effective = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_EFFECTIVE_CONFIG_INVALID, "effective");
|
||||||
assert_eq!(classify_error(&effective), STAGE_EFFECTIVE);
|
assert_eq!(super::classify_error(&effective), super::STAGE_EFFECTIVE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn schema_source_read_errors_are_classified_as_schema_failures() {
|
fn schema_source_read_errors_are_classified_as_schema_failures() {
|
||||||
let error = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_JSON_FILE_READ_FAILED, "missing schema").with_context("file_id", "schema.std.logging");
|
let error = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_JSON_FILE_READ_FAILED, "missing schema").with_context("file_id", "schema.std.logging");
|
||||||
assert_eq!(classify_error(&error), STAGE_SCHEMA);
|
assert_eq!(super::classify_error(&error), super::STAGE_SCHEMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn document_error_projection_does_not_expose_arbitrary_error_context() {
|
fn document_error_projection_does_not_expose_arbitrary_error_context() {
|
||||||
let error = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_JSON_SYNTAX_INVALID, "invalid source").with_context("detail", "sensitive-canary");
|
let error = ksp_core_lib::Error::new(ksp_config_lib::ERROR_CODE_JSON_SYNTAX_INVALID, "invalid source").with_context("detail", "sensitive-canary");
|
||||||
let projection = ConfigDocumentErrorDto::from_error(&error);
|
let projection = crate::ConfigDocumentErrorDto::from_error(&error);
|
||||||
assert_eq!(projection.diagnostic_stage, STAGE_JSON);
|
assert_eq!(projection.diagnostic_stage, super::STAGE_JSON);
|
||||||
assert_eq!(projection.error.domain, "config");
|
assert_eq!(projection.error.domain, "config");
|
||||||
assert_eq!(projection.error.code, "json_syntax_invalid");
|
assert_eq!(projection.error.code, "json_syntax_invalid");
|
||||||
assert_eq!(projection.error.message, "invalid source");
|
assert_eq!(projection.error.message, "invalid source");
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// file: crates/ksp-app-config-desk/unit_tests/dto_common.rs
|
// file: crates/ksp-app-config-desk/unit_tests/dto_common.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn command_error_projection_excludes_context_values() {
|
fn command_error_projection_excludes_context_values() {
|
||||||
let error = ksp_core_lib::Error::new(ksp_core_lib::ErrorCode::new("test", "failure"), "safe message").with_context("secret_canary", "must-not-be-exported");
|
let error = ksp_core_lib::Error::new(ksp_core_lib::ErrorCode::new("test", "failure"), "safe message").with_context("secret_canary", "must-not-be-exported");
|
||||||
let dto = super::CommandErrorDto::from_error(&error);
|
let dto = crate::CommandErrorDto::from_error(&error);
|
||||||
assert_eq!(dto.domain, "test");
|
assert_eq!(dto.domain, "test");
|
||||||
assert_eq!(dto.code, "failure");
|
assert_eq!(dto.code, "failure");
|
||||||
assert_eq!(dto.message, "safe message");
|
assert_eq!(dto.message, "safe message");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-config-desk/unit_tests/environment.rs
|
// file: crates/ksp-app-config-desk/unit_tests/environment.rs
|
||||||
// version: 2
|
// version: 4
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn namespace_projection_distinguishes_ksp_and_kspb_sensitivity_families() {
|
fn namespace_projection_distinguishes_ksp_and_kspb_sensitivity_families() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-config-desk/unit_tests/logging_editor.rs
|
// file: crates/ksp-app-config-desk/unit_tests/logging_editor.rs
|
||||||
// version: 4
|
// version: 7
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn committed_logging_document_maps_complete_read_only_editor_contract() {
|
fn committed_logging_document_maps_complete_read_only_editor_contract() {
|
||||||
@@ -66,7 +66,7 @@ fn editor_candidate_round_trips_through_typed_config_contract() {
|
|||||||
let document = super::document_from_management(&management);
|
let document = super::document_from_management(&management);
|
||||||
assert!(document.is_ok(), "Logging editor document should load: {document:?}");
|
assert!(document.is_ok(), "Logging editor document should load: {document:?}");
|
||||||
if let std::result::Result::Ok(document) = document {
|
if let std::result::Result::Ok(document) = document {
|
||||||
let candidate = super::LoggingDocumentCandidateDto {
|
let candidate = crate::LoggingDocumentCandidateDto {
|
||||||
logs_directory: document.logs_directory.clone(),
|
logs_directory: document.logs_directory.clone(),
|
||||||
default_profile: document.default_profile.clone(),
|
default_profile: document.default_profile.clone(),
|
||||||
profiles: document.profiles.clone(),
|
profiles: document.profiles.clone(),
|
||||||
@@ -89,42 +89,42 @@ fn editor_candidate_round_trips_through_typed_config_contract() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn profile_candidate_preserves_multi_file_and_target_filter_shape() {
|
fn profile_candidate_preserves_multi_file_and_target_filter_shape() {
|
||||||
let profile = super::LoggingProfileDto {
|
let profile = crate::LoggingProfileDto {
|
||||||
profile_id: "clone_test".to_owned(),
|
profile_id: "clone_test".to_owned(),
|
||||||
default_filter: "info".to_owned(),
|
default_filter: "info".to_owned(),
|
||||||
span_events: "full".to_owned(),
|
span_events: "full".to_owned(),
|
||||||
console: super::LoggingConsoleDto {
|
console: crate::LoggingConsoleDto {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
output: "stdout".to_owned(),
|
output: "stdout".to_owned(),
|
||||||
ansi: false,
|
ansi: false,
|
||||||
format: "human".to_owned(),
|
format: "human".to_owned(),
|
||||||
filter: super::LoggingOutputFilterDto { level: "info".to_owned(), targets: std::vec!["*".to_owned()], domains: std::vec!["*".to_owned()] },
|
filter: crate::LoggingOutputFilterDto { level: "info".to_owned(), targets: std::vec!["*".to_owned()], domains: std::vec!["*".to_owned()] },
|
||||||
},
|
},
|
||||||
files: std::vec![
|
files: std::vec![
|
||||||
super::LoggingFileDto {
|
crate::LoggingFileDto {
|
||||||
output_id: "file.one".to_owned(),
|
output_id: "file.one".to_owned(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
path: "one.log".to_owned(),
|
path: "one.log".to_owned(),
|
||||||
rotation: "daily".to_owned(),
|
rotation: "daily".to_owned(),
|
||||||
format: "human".to_owned(),
|
format: "human".to_owned(),
|
||||||
ansi: false,
|
ansi: false,
|
||||||
filter: super::LoggingOutputFilterDto { level: "debug".to_owned(), targets: std::vec!["*".to_owned()], domains: std::vec!["*".to_owned()] },
|
filter: crate::LoggingOutputFilterDto { level: "debug".to_owned(), targets: std::vec!["*".to_owned()], domains: std::vec!["*".to_owned()] },
|
||||||
},
|
},
|
||||||
super::LoggingFileDto {
|
crate::LoggingFileDto {
|
||||||
output_id: "file.two".to_owned(),
|
output_id: "file.two".to_owned(),
|
||||||
enabled: false,
|
enabled: false,
|
||||||
path: "two.jsonl".to_owned(),
|
path: "two.jsonl".to_owned(),
|
||||||
rotation: "hourly".to_owned(),
|
rotation: "hourly".to_owned(),
|
||||||
format: "json".to_owned(),
|
format: "json".to_owned(),
|
||||||
ansi: false,
|
ansi: false,
|
||||||
filter: super::LoggingOutputFilterDto {
|
filter: crate::LoggingOutputFilterDto {
|
||||||
level: "error".to_owned(),
|
level: "error".to_owned(),
|
||||||
targets: std::vec!["ksp-config-lib".to_owned()],
|
targets: std::vec!["ksp-config-lib".to_owned()],
|
||||||
domains: std::vec!["config".to_owned()],
|
domains: std::vec!["config".to_owned()],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
target_filters: std::vec![super::LoggingTargetFilterDto { target_prefix: "ksp-app-config-desk".to_owned(), level: "trace".to_owned() }],
|
target_filters: std::vec![crate::LoggingTargetFilterDto { target_prefix: "ksp-app-config-desk".to_owned(), level: "trace".to_owned() }],
|
||||||
};
|
};
|
||||||
let config = super::config_profile(profile);
|
let config = super::config_profile(profile);
|
||||||
assert_eq!(config.profile_id(), "clone_test");
|
assert_eq!(config.profile_id(), "clone_test");
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-app-config-desk/unit_tests/logging_runtime.rs
|
// file: crates/ksp-app-config-desk/unit_tests/logging_runtime.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn launch_identity_is_safe_and_contains_application_and_process_identity() {
|
fn launch_identity_is_safe_and_contains_application_and_process_identity() {
|
||||||
let identity = super::launch_identity();
|
let identity = crate::launch_identity();
|
||||||
assert!(identity.is_ok());
|
assert!(identity.is_ok());
|
||||||
if let std::result::Result::Ok(identity) = identity {
|
if let std::result::Result::Ok(identity) = identity {
|
||||||
assert_eq!(identity.application_id(), crate::TRACING_TARGET);
|
assert_eq!(identity.application_id(), crate::TRACING_TARGET);
|
||||||
@@ -21,6 +21,6 @@ fn runtime_rotation_labels_are_stable() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn runtime_count_projection_is_non_lossy_for_normal_values() {
|
fn runtime_count_projection_is_non_lossy_for_normal_values() {
|
||||||
assert_eq!(super::count_to_u64(0), 0);
|
assert_eq!(crate::count_to_u64(0), 0);
|
||||||
assert_eq!(super::count_to_u64(42), 42);
|
assert_eq!(crate::count_to_u64(42), 42);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,23 @@
|
|||||||
// file: crates/ksp-app-config-desk/unit_tests/logging_test.rs
|
// file: crates/ksp-app-config-desk/unit_tests/logging_test.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
use super::{LoggingTestLevel, LoggingTestTarget};
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn controlled_logging_test_levels_include_all_five_levels_and_batch_mode() {
|
fn controlled_logging_test_levels_include_all_five_levels_and_batch_mode() {
|
||||||
assert!(matches!(super::parse_level("trace"), std::result::Result::Ok(LoggingTestLevel::Trace)));
|
assert!(matches!(super::parse_level("trace"), std::result::Result::Ok(super::LoggingTestLevel::Trace)));
|
||||||
assert!(matches!(super::parse_level("debug"), std::result::Result::Ok(LoggingTestLevel::Debug)));
|
assert!(matches!(super::parse_level("debug"), std::result::Result::Ok(super::LoggingTestLevel::Debug)));
|
||||||
assert!(matches!(super::parse_level("info"), std::result::Result::Ok(LoggingTestLevel::Info)));
|
assert!(matches!(super::parse_level("info"), std::result::Result::Ok(super::LoggingTestLevel::Info)));
|
||||||
assert!(matches!(super::parse_level("warn"), std::result::Result::Ok(LoggingTestLevel::Warn)));
|
assert!(matches!(super::parse_level("warn"), std::result::Result::Ok(super::LoggingTestLevel::Warn)));
|
||||||
assert!(matches!(super::parse_level("error"), std::result::Result::Ok(LoggingTestLevel::Error)));
|
assert!(matches!(super::parse_level("error"), std::result::Result::Ok(super::LoggingTestLevel::Error)));
|
||||||
assert!(matches!(super::parse_level("all"), std::result::Result::Ok(LoggingTestLevel::All)));
|
assert!(matches!(super::parse_level("all"), std::result::Result::Ok(super::LoggingTestLevel::All)));
|
||||||
assert!(super::parse_level("fatal").is_err());
|
assert!(super::parse_level("fatal").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn controlled_logging_test_targets_are_static_ksp_targets() {
|
fn controlled_logging_test_targets_are_static_ksp_targets() {
|
||||||
assert!(matches!(super::parse_target("app"), std::result::Result::Ok(LoggingTestTarget::App)));
|
assert!(matches!(super::parse_target("app"), std::result::Result::Ok(super::LoggingTestTarget::App)));
|
||||||
assert!(matches!(super::parse_target("logging_test"), std::result::Result::Ok(LoggingTestTarget::Dedicated)));
|
assert!(matches!(super::parse_target("logging_test"), std::result::Result::Ok(super::LoggingTestTarget::Dedicated)));
|
||||||
assert_eq!(super::target_value(LoggingTestTarget::App), crate::TRACING_TARGET);
|
assert_eq!(super::target_value(super::LoggingTestTarget::App), crate::TRACING_TARGET);
|
||||||
assert_eq!(super::target_value(LoggingTestTarget::Dedicated), crate::TRACING_TARGET_LOGGING_TEST);
|
assert_eq!(super::target_value(super::LoggingTestTarget::Dedicated), crate::TRACING_TARGET_LOGGING_TEST);
|
||||||
assert!(super::parse_target("external").is_err());
|
assert!(super::parse_target("external").is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-config-desk/unit_tests/profiles.rs
|
// file: crates/ksp-app-config-desk/unit_tests/profiles.rs
|
||||||
// version: 3
|
// version: 5
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn profile_inventory_exposes_registered_standard_profile_documents() {
|
fn profile_inventory_exposes_registered_standard_profile_documents() {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-app-config-desk/unit_tests/secrets.rs
|
// file: crates/ksp-app-config-desk/unit_tests/secrets.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reveal_policy_accepts_only_secret_names_and_supported_sources() {
|
fn reveal_policy_accepts_only_secret_names_and_supported_sources() {
|
||||||
@@ -21,7 +21,7 @@ fn reveal_policy_accepts_only_secret_names_and_supported_sources() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn reveal_response_type_does_not_require_debug_or_clone_contracts() {
|
fn reveal_response_type_does_not_require_debug_or_clone_contracts() {
|
||||||
let response = super::SecretRevealResponseDto {
|
let response = crate::SecretRevealResponseDto {
|
||||||
variable_name: "KSP_SECRET_TEST_VALUE".to_owned(),
|
variable_name: "KSP_SECRET_TEST_VALUE".to_owned(),
|
||||||
source: "dotenv".to_owned(),
|
source: "dotenv".to_owned(),
|
||||||
value: std::option::Option::Some("secret-canary".to_owned()),
|
value: std::option::Option::Some("secret-canary".to_owned()),
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
// file: crates/ksp-config-lib/src/bootstrap.rs
|
// file: crates/ksp-config-lib/src/bootstrap.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// Default root containing KSP runtime configuration documents.
|
|
||||||
pub const DEFAULT_CFG_PATH: &str = "config";
|
|
||||||
/// Default root containing KSP JSON schemas.
|
|
||||||
pub const DEFAULT_SCHEMA_PATH: &str = "config/schemas";
|
|
||||||
/// Bootstrap argument used to replace the configuration document root.
|
/// Bootstrap argument used to replace the configuration document root.
|
||||||
pub const ARG_CFG_PATH: &str = "--cfgpath";
|
pub const ARG_CFG_PATH: &str = "--cfgpath";
|
||||||
/// Bootstrap argument used to replace the schema root.
|
/// Bootstrap argument used to replace the schema root.
|
||||||
pub const ARG_SCHEMA_PATH: &str = "--schemapath";
|
pub const ARG_SCHEMA_PATH: &str = "--schemapath";
|
||||||
|
/// Default root containing KSP runtime configuration documents.
|
||||||
|
pub const DEFAULT_CFG_PATH: &str = "config";
|
||||||
|
/// Default root containing KSP JSON schemas.
|
||||||
|
pub const DEFAULT_SCHEMA_PATH: &str = "config/schemas";
|
||||||
|
|
||||||
/// Non-recursive bootstrap options required before Config can resolve any managed document.
|
/// Non-recursive bootstrap options required before Config can resolve any managed document.
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-config-lib/src/composite.rs
|
// file: crates/ksp-config-lib/src/composite.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// One resolved document component selected by a composite profile.
|
/// One resolved document component selected by a composite profile.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@@ -118,6 +118,7 @@ struct CompositeDocumentReferenceSource {
|
|||||||
profile_id: std::option::Option<String>,
|
profile_id: std::option::Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates composite document contract.
|
||||||
pub(crate) fn validate_composite_document_contract(engine: &crate::ConfigDocumentEngine, document: &crate::ConfigJsonDocument) -> ksp_core_lib::Result<()> {
|
pub(crate) fn validate_composite_document_contract(engine: &crate::ConfigDocumentEngine, document: &crate::ConfigJsonDocument) -> ksp_core_lib::Result<()> {
|
||||||
if !document.file_id().as_str().starts_with("cfg.composite.") {
|
if !document.file_id().as_str().starts_with("cfg.composite.") {
|
||||||
return std::result::Result::Ok(());
|
return std::result::Result::Ok(());
|
||||||
@@ -198,12 +199,8 @@ fn resolve_composite_document(
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let resolved = crate::profile::load_resolved_profile_with_source(
|
let resolved =
|
||||||
engine,
|
crate::load_resolved_profile_with_source(engine, &file_id, reference.profile_id.as_deref(), crate::ConfigProfileSelectionSource::Composite);
|
||||||
&file_id,
|
|
||||||
reference.profile_id.as_deref(),
|
|
||||||
crate::ConfigProfileSelectionSource::Composite,
|
|
||||||
);
|
|
||||||
let resolved = match resolved {
|
let resolved = match resolved {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
@@ -262,8 +259,7 @@ fn validate_reference(
|
|||||||
"referenced file_id is not a Config document",
|
"referenced file_id is not a Config document",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
let resolved =
|
let resolved = crate::load_resolved_profile_with_source(engine, &file_id, reference.profile_id.as_deref(), crate::ConfigProfileSelectionSource::Composite);
|
||||||
crate::profile::load_resolved_profile_with_source(engine, &file_id, reference.profile_id.as_deref(), crate::ConfigProfileSelectionSource::Composite);
|
|
||||||
if let std::result::Result::Err(error) = resolved {
|
if let std::result::Result::Err(error) = resolved {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-config-lib/src/document.rs
|
// file: crates/ksp-config-lib/src/document.rs
|
||||||
// version: 5
|
// version: 6
|
||||||
|
|
||||||
/// A Config-managed JSON document that has passed syntax, schema and current semantic validation.
|
/// A Config-managed JSON document that has passed syntax, schema and current semantic validation.
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
@@ -85,6 +85,7 @@ impl ConfigDocumentEngine {
|
|||||||
return self.validate_document(document, &schema_file_id);
|
return self.validate_document(document, &schema_file_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates source candidate.
|
||||||
pub(crate) fn validate_source_candidate(&self, file_id: &crate::ConfigFileId, source: &str) -> ksp_core_lib::Result<ConfigJsonDocument> {
|
pub(crate) fn validate_source_candidate(&self, file_id: &crate::ConfigFileId, source: &str) -> ksp_core_lib::Result<ConfigJsonDocument> {
|
||||||
let path = self.registry.resolve_path(&self.bootstrap, file_id);
|
let path = self.registry.resolve_path(&self.bootstrap, file_id);
|
||||||
let path = match path {
|
let path = match path {
|
||||||
@@ -99,6 +100,7 @@ impl ConfigDocumentEngine {
|
|||||||
return self.validate_candidate(file_id, value);
|
return self.validate_candidate(file_id, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates candidate.
|
||||||
pub(crate) fn validate_candidate(&self, file_id: &crate::ConfigFileId, value: serde_json::Value) -> ksp_core_lib::Result<ConfigJsonDocument> {
|
pub(crate) fn validate_candidate(&self, file_id: &crate::ConfigFileId, value: serde_json::Value) -> ksp_core_lib::Result<ConfigJsonDocument> {
|
||||||
let descriptor = self.registry.descriptor(file_id);
|
let descriptor = self.registry.descriptor(file_id);
|
||||||
let descriptor = match descriptor {
|
let descriptor = match descriptor {
|
||||||
@@ -256,11 +258,11 @@ fn validate_instance(document: &ConfigJsonDocument, schema: &ConfigJsonDocument)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn validate_document_semantics(engine: &ConfigDocumentEngine, document: &ConfigJsonDocument) -> ksp_core_lib::Result<()> {
|
fn validate_document_semantics(engine: &ConfigDocumentEngine, document: &ConfigJsonDocument) -> ksp_core_lib::Result<()> {
|
||||||
let profile_validation = crate::profile::validate_document_profile_contract(document);
|
let profile_validation = crate::validate_document_profile_contract(document);
|
||||||
if let std::result::Result::Err(error) = profile_validation {
|
if let std::result::Result::Err(error) = profile_validation {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
let composite_validation = crate::composite::validate_composite_document_contract(engine, document);
|
let composite_validation = crate::validate_composite_document_contract(engine, document);
|
||||||
if let std::result::Result::Err(error) = composite_validation {
|
if let std::result::Result::Err(error) = composite_validation {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// file: crates/ksp-config-lib/src/environment.rs
|
// file: crates/ksp-config-lib/src/environment.rs
|
||||||
// version: 5
|
// version: 8
|
||||||
|
|
||||||
/// Default local environment file read by Config from the process launch directory.
|
|
||||||
pub const DEFAULT_DOTENV_PATH: &str = ".env";
|
|
||||||
|
|
||||||
/// Versioned environment contract template expected at the repository/runtime root.
|
/// Versioned environment contract template expected at the repository/runtime root.
|
||||||
pub const DEFAULT_DOTENV_EXAMPLE_PATH: &str = ".env.example";
|
pub const DEFAULT_DOTENV_EXAMPLE_PATH: &str = ".env.example";
|
||||||
|
/// Default local environment file read by Config from the process launch directory.
|
||||||
|
pub const DEFAULT_DOTENV_PATH: &str = ".env";
|
||||||
|
|
||||||
const LOGGING_DOMAIN: &str = "config.environment";
|
const LOGGING_DOMAIN: &str = "config.environment";
|
||||||
|
|
||||||
@@ -230,6 +229,7 @@ impl ConfigEnvironment {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads from dotenv path.
|
||||||
pub(crate) fn load_from_dotenv_path(dotenv_path: &std::path::Path) -> ksp_core_lib::Result<Self> {
|
pub(crate) fn load_from_dotenv_path(dotenv_path: &std::path::Path) -> ksp_core_lib::Result<Self> {
|
||||||
let process = collect_process_environment(std::env::vars_os());
|
let process = collect_process_environment(std::env::vars_os());
|
||||||
let process = match process {
|
let process = match process {
|
||||||
@@ -244,57 +244,24 @@ impl ConfigEnvironment {
|
|||||||
return std::result::Result::Ok(Self { process, dotenv, dotenv_path: dotenv_path.to_path_buf() });
|
return std::result::Result::Ok(Self { process, dotenv, dotenv_path: dotenv_path.to_path_buf() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current process values.
|
||||||
pub(crate) const fn process_values(&self) -> &std::collections::BTreeMap<String, String> {
|
pub(crate) const fn process_values(&self) -> &std::collections::BTreeMap<String, String> {
|
||||||
return &self.process;
|
return &self.process;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the current dotenv values.
|
||||||
pub(crate) const fn dotenv_values(&self) -> &std::collections::BTreeMap<String, String> {
|
pub(crate) const fn dotenv_values(&self) -> &std::collections::BTreeMap<String, String> {
|
||||||
return &self.dotenv;
|
return &self.dotenv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Builds `ConfigEnvironment` from maps.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn from_maps(process: std::collections::BTreeMap<String, String>, dotenv: std::collections::BTreeMap<String, String>) -> Self {
|
pub(crate) fn from_maps(process: std::collections::BTreeMap<String, String>, dotenv: std::collections::BTreeMap<String, String>) -> Self {
|
||||||
return Self { process, dotenv, dotenv_path: std::path::PathBuf::from(DEFAULT_DOTENV_PATH) };
|
return Self { process, dotenv, dotenv_path: std::path::PathBuf::from(DEFAULT_DOTENV_PATH) };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn collect_process_environment<I>(values: I) -> ksp_core_lib::Result<std::collections::BTreeMap<String, String>>
|
/// Executes the crate-internal parse dotenv content operation for the owning module.
|
||||||
where
|
|
||||||
I: std::iter::IntoIterator<Item = (std::ffi::OsString, std::ffi::OsString)>,
|
|
||||||
{
|
|
||||||
let mut output = std::collections::BTreeMap::<String, String>::new();
|
|
||||||
for (name, value) in values {
|
|
||||||
let name = match name.to_str() {
|
|
||||||
std::option::Option::Some(value) => value,
|
|
||||||
std::option::Option::None => continue,
|
|
||||||
};
|
|
||||||
if !has_supported_namespace(name) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
let validation = validate_supported_variable_name(name);
|
|
||||||
if let std::result::Result::Err(error) = validation {
|
|
||||||
return std::result::Result::Err(error);
|
|
||||||
}
|
|
||||||
let value = value.into_string();
|
|
||||||
let value = match value {
|
|
||||||
std::result::Result::Ok(value) => value,
|
|
||||||
std::result::Result::Err(_) => return std::result::Result::Err(invalid_environment_value_error(name)),
|
|
||||||
};
|
|
||||||
output.insert(name.to_owned(), value);
|
|
||||||
}
|
|
||||||
return std::result::Result::Ok(output);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_dotenv_file(path: &std::path::Path) -> ksp_core_lib::Result<std::collections::BTreeMap<String, String>> {
|
|
||||||
let content = std::fs::read_to_string(path);
|
|
||||||
let content = match content {
|
|
||||||
std::result::Result::Ok(value) => value,
|
|
||||||
std::result::Result::Err(error) if error.kind() == std::io::ErrorKind::NotFound => return std::result::Result::Ok(std::collections::BTreeMap::new()),
|
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(dotenv_read_error(path, error)),
|
|
||||||
};
|
|
||||||
return parse_dotenv_content(path, content.as_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn parse_dotenv_content(path: &std::path::Path, content: &str) -> ksp_core_lib::Result<std::collections::BTreeMap<String, String>> {
|
pub(crate) fn parse_dotenv_content(path: &std::path::Path, content: &str) -> ksp_core_lib::Result<std::collections::BTreeMap<String, String>> {
|
||||||
let mut output = std::collections::BTreeMap::<String, String>::new();
|
let mut output = std::collections::BTreeMap::<String, String>::new();
|
||||||
for (line_index, raw_line) in content.lines().enumerate() {
|
for (line_index, raw_line) in content.lines().enumerate() {
|
||||||
@@ -342,6 +309,61 @@ pub(crate) fn parse_dotenv_content(path: &std::path::Path, content: &str) -> ksp
|
|||||||
return std::result::Result::Ok(output);
|
return std::result::Result::Ok(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates supported variable name.
|
||||||
|
pub(crate) fn validate_supported_variable_name(variable_name: &str) -> ksp_core_lib::Result<()> {
|
||||||
|
if !has_supported_namespace(variable_name) {
|
||||||
|
return std::result::Result::Err(invalid_variable_error(variable_name, "variable must use the KSP_ or KSPB_ namespace"));
|
||||||
|
}
|
||||||
|
let prefix_length = if variable_name.starts_with("KSPB_") { 5 } else { 4 };
|
||||||
|
if variable_name.len() <= prefix_length {
|
||||||
|
return std::result::Result::Err(invalid_variable_error(variable_name, "variable namespace must be followed by a name"));
|
||||||
|
}
|
||||||
|
for byte in variable_name.bytes() {
|
||||||
|
let valid = byte.is_ascii_uppercase() || byte.is_ascii_digit() || byte == b'_';
|
||||||
|
if !valid {
|
||||||
|
return std::result::Result::Err(invalid_variable_error(variable_name, "variable names use uppercase ASCII letters, digits and underscores"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect_process_environment<I>(values: I) -> ksp_core_lib::Result<std::collections::BTreeMap<String, String>>
|
||||||
|
where
|
||||||
|
I: std::iter::IntoIterator<Item = (std::ffi::OsString, std::ffi::OsString)>,
|
||||||
|
{
|
||||||
|
let mut output = std::collections::BTreeMap::<String, String>::new();
|
||||||
|
for (name, value) in values {
|
||||||
|
let name = match name.to_str() {
|
||||||
|
std::option::Option::Some(value) => value,
|
||||||
|
std::option::Option::None => continue,
|
||||||
|
};
|
||||||
|
if !has_supported_namespace(name) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let validation = validate_supported_variable_name(name);
|
||||||
|
if let std::result::Result::Err(error) = validation {
|
||||||
|
return std::result::Result::Err(error);
|
||||||
|
}
|
||||||
|
let value = value.into_string();
|
||||||
|
let value = match value {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(_) => return std::result::Result::Err(invalid_environment_value_error(name)),
|
||||||
|
};
|
||||||
|
output.insert(name.to_owned(), value);
|
||||||
|
}
|
||||||
|
return std::result::Result::Ok(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_dotenv_file(path: &std::path::Path) -> ksp_core_lib::Result<std::collections::BTreeMap<String, String>> {
|
||||||
|
let content = std::fs::read_to_string(path);
|
||||||
|
let content = match content {
|
||||||
|
std::result::Result::Ok(value) => value,
|
||||||
|
std::result::Result::Err(error) if error.kind() == std::io::ErrorKind::NotFound => return std::result::Result::Ok(std::collections::BTreeMap::new()),
|
||||||
|
std::result::Result::Err(error) => return std::result::Result::Err(dotenv_read_error(path, error)),
|
||||||
|
};
|
||||||
|
return crate::parse_dotenv_content(path, content.as_str());
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_dotenv_value(path: &std::path::Path, line_number: usize, raw_value: &str) -> ksp_core_lib::Result<String> {
|
fn parse_dotenv_value(path: &std::path::Path, line_number: usize, raw_value: &str) -> ksp_core_lib::Result<String> {
|
||||||
if raw_value.starts_with('\'') && (raw_value.len() < 2 || !raw_value.ends_with('\'')) {
|
if raw_value.starts_with('\'') && (raw_value.len() < 2 || !raw_value.ends_with('\'')) {
|
||||||
return std::result::Result::Err(dotenv_syntax_error(path, line_number, "single-quoted value is not terminated"));
|
return std::result::Result::Err(dotenv_syntax_error(path, line_number, "single-quoted value is not terminated"));
|
||||||
@@ -516,23 +538,6 @@ fn escape_json_pointer_token(value: &str) -> String {
|
|||||||
return value.replace('~', "~0").replace('/', "~1");
|
return value.replace('~', "~0").replace('/', "~1");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn validate_supported_variable_name(variable_name: &str) -> ksp_core_lib::Result<()> {
|
|
||||||
if !has_supported_namespace(variable_name) {
|
|
||||||
return std::result::Result::Err(invalid_variable_error(variable_name, "variable must use the KSP_ or KSPB_ namespace"));
|
|
||||||
}
|
|
||||||
let prefix_length = if variable_name.starts_with("KSPB_") { 5 } else { 4 };
|
|
||||||
if variable_name.len() <= prefix_length {
|
|
||||||
return std::result::Result::Err(invalid_variable_error(variable_name, "variable namespace must be followed by a name"));
|
|
||||||
}
|
|
||||||
for byte in variable_name.bytes() {
|
|
||||||
let valid = byte.is_ascii_uppercase() || byte.is_ascii_digit() || byte == b'_';
|
|
||||||
if !valid {
|
|
||||||
return std::result::Result::Err(invalid_variable_error(variable_name, "variable names use uppercase ASCII letters, digits and underscores"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::result::Result::Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_supported_namespace(variable_name: &str) -> bool {
|
fn has_supported_namespace(variable_name: &str) -> bool {
|
||||||
return variable_name.starts_with("KSP_") || variable_name.starts_with("KSPB_");
|
return variable_name.starts_with("KSP_") || variable_name.starts_with("KSPB_");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,68 +1,47 @@
|
|||||||
// file: crates/ksp-config-lib/src/error.rs
|
// file: crates/ksp-config-lib/src/error.rs
|
||||||
// version: 8
|
// version: 9
|
||||||
|
|
||||||
/// Error code used when a Config bootstrap argument is missing its value.
|
/// Error code used when a Config bootstrap argument is missing its value.
|
||||||
pub const ERROR_CODE_BOOTSTRAP_ARGUMENT_MISSING_VALUE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "bootstrap_argument_missing_value");
|
pub const ERROR_CODE_BOOTSTRAP_ARGUMENT_MISSING_VALUE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "bootstrap_argument_missing_value");
|
||||||
|
|
||||||
/// Error code used when a Config bootstrap path is empty, inaccessible, or resolves to an existing non-directory path.
|
/// Error code used when a Config bootstrap path is empty, inaccessible, or resolves to an existing non-directory path.
|
||||||
pub const ERROR_CODE_BOOTSTRAP_INVALID_PATH: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "bootstrap_invalid_path");
|
pub const ERROR_CODE_BOOTSTRAP_INVALID_PATH: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "bootstrap_invalid_path");
|
||||||
|
|
||||||
/// Error code used when a logical Config file identifier is malformed.
|
|
||||||
pub const ERROR_CODE_FILE_ID_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "file_id_invalid");
|
|
||||||
|
|
||||||
/// Error code used when a requested logical Config file identifier is not registered.
|
|
||||||
pub const ERROR_CODE_FILE_ID_UNKNOWN: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "file_id_unknown");
|
|
||||||
|
|
||||||
/// Error code used when the same logical Config file identifier is registered more than once.
|
|
||||||
pub const ERROR_CODE_FILE_ID_DUPLICATE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "file_id_duplicate");
|
|
||||||
|
|
||||||
/// Error code used when a Config filename mapping or descriptor relation is invalid.
|
|
||||||
pub const ERROR_CODE_FILE_MAPPING_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "file_mapping_invalid");
|
|
||||||
|
|
||||||
/// Error code used when a Config-managed JSON document or schema cannot be read from its resolved path.
|
|
||||||
pub const ERROR_CODE_JSON_FILE_READ_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "json_file_read_failed");
|
|
||||||
|
|
||||||
/// Error code used when a Config-managed file contains invalid JSON syntax.
|
|
||||||
pub const ERROR_CODE_JSON_SYNTAX_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "json_syntax_invalid");
|
|
||||||
|
|
||||||
/// Error code used when a JSON Schema document is itself invalid for the selected JSON Schema draft.
|
|
||||||
pub const ERROR_CODE_SCHEMA_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "schema_invalid");
|
|
||||||
|
|
||||||
/// Error code used when a Config document does not satisfy its registered JSON Schema.
|
|
||||||
pub const ERROR_CODE_SCHEMA_VALIDATION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "schema_validation_failed");
|
|
||||||
|
|
||||||
/// Error code used when a schema-valid Config document violates KSP semantic invariants for its document type.
|
|
||||||
pub const ERROR_CODE_DOCUMENT_SEMANTIC_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "document_semantic_invalid");
|
|
||||||
|
|
||||||
/// Error code used when an explicitly requested Config profile does not exist in a validated document.
|
|
||||||
pub const ERROR_CODE_PROFILE_NOT_FOUND: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "profile_not_found");
|
|
||||||
|
|
||||||
/// Error code used when a composite document references an invalid, unknown, or unsupported Config document.
|
/// Error code used when a composite document references an invalid, unknown, or unsupported Config document.
|
||||||
pub const ERROR_CODE_COMPOSITE_REFERENCE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "composite_reference_invalid");
|
pub const ERROR_CODE_COMPOSITE_REFERENCE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "composite_reference_invalid");
|
||||||
|
/// Error code used when a schema-valid Config document violates KSP semantic invariants for its document type.
|
||||||
|
pub const ERROR_CODE_DOCUMENT_SEMANTIC_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "document_semantic_invalid");
|
||||||
/// Error code used when the local `.env` file cannot be read for a reason other than absence.
|
/// Error code used when the local `.env` file cannot be read for a reason other than absence.
|
||||||
pub const ERROR_CODE_DOTENV_FILE_READ_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "dotenv_file_read_failed");
|
pub const ERROR_CODE_DOTENV_FILE_READ_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "dotenv_file_read_failed");
|
||||||
|
|
||||||
/// Error code used when the local `.env` file contains syntax Config cannot interpret safely.
|
/// Error code used when the local `.env` file contains syntax Config cannot interpret safely.
|
||||||
pub const ERROR_CODE_DOTENV_SYNTAX_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "dotenv_syntax_invalid");
|
pub const ERROR_CODE_DOTENV_SYNTAX_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "dotenv_syntax_invalid");
|
||||||
|
|
||||||
/// Error code used when a Config environment variable name is malformed or outside the KSP/KSPB namespaces.
|
|
||||||
pub const ERROR_CODE_ENVIRONMENT_VARIABLE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "environment_variable_invalid");
|
|
||||||
|
|
||||||
/// Error code used when a referenced Config environment variable is absent and has no fallback.
|
|
||||||
pub const ERROR_CODE_ENVIRONMENT_VARIABLE_MISSING: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "environment_variable_missing");
|
|
||||||
|
|
||||||
/// Error code used when a supported process environment variable has a value that cannot become a JSON UTF-8 string.
|
|
||||||
pub const ERROR_CODE_ENVIRONMENT_VALUE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "environment_value_invalid");
|
|
||||||
|
|
||||||
/// Error code used when a `${NAME}` / `${NAME:-fallback}` expression is malformed.
|
|
||||||
pub const ERROR_CODE_ENVIRONMENT_PLACEHOLDER_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "environment_placeholder_invalid");
|
|
||||||
|
|
||||||
/// Error code used when an environment-resolved Config cannot be mapped safely to a runtime consumer contract.
|
/// Error code used when an environment-resolved Config cannot be mapped safely to a runtime consumer contract.
|
||||||
pub const ERROR_CODE_EFFECTIVE_CONFIG_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "effective_config_invalid");
|
pub const ERROR_CODE_EFFECTIVE_CONFIG_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "effective_config_invalid");
|
||||||
|
/// Error code used when a `${NAME}` / `${NAME:-fallback}` expression is malformed.
|
||||||
|
pub const ERROR_CODE_ENVIRONMENT_PLACEHOLDER_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "environment_placeholder_invalid");
|
||||||
|
/// Error code used when a supported process environment variable has a value that cannot become a JSON UTF-8 string.
|
||||||
|
pub const ERROR_CODE_ENVIRONMENT_VALUE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "environment_value_invalid");
|
||||||
|
/// Error code used when a Config environment variable name is malformed or outside the KSP/KSPB namespaces.
|
||||||
|
pub const ERROR_CODE_ENVIRONMENT_VARIABLE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "environment_variable_invalid");
|
||||||
|
/// Error code used when a referenced Config environment variable is absent and has no fallback.
|
||||||
|
pub const ERROR_CODE_ENVIRONMENT_VARIABLE_MISSING: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "environment_variable_missing");
|
||||||
|
/// Error code used when the same logical Config file identifier is registered more than once.
|
||||||
|
pub const ERROR_CODE_FILE_ID_DUPLICATE: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "file_id_duplicate");
|
||||||
|
/// Error code used when a logical Config file identifier is malformed.
|
||||||
|
pub const ERROR_CODE_FILE_ID_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "file_id_invalid");
|
||||||
|
/// Error code used when a requested logical Config file identifier is not registered.
|
||||||
|
pub const ERROR_CODE_FILE_ID_UNKNOWN: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "file_id_unknown");
|
||||||
|
/// Error code used when a Config filename mapping or descriptor relation is invalid.
|
||||||
|
pub const ERROR_CODE_FILE_MAPPING_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "file_mapping_invalid");
|
||||||
|
/// Error code used when a Config-managed JSON document or schema cannot be read from its resolved path.
|
||||||
|
pub const ERROR_CODE_JSON_FILE_READ_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "json_file_read_failed");
|
||||||
|
/// Error code used when a Config-managed file contains invalid JSON syntax.
|
||||||
|
pub const ERROR_CODE_JSON_SYNTAX_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "json_syntax_invalid");
|
||||||
/// Error code used when an explicit Config management operation is unsupported or targets the wrong managed resource kind.
|
/// Error code used when an explicit Config management operation is unsupported or targets the wrong managed resource kind.
|
||||||
pub const ERROR_CODE_MANAGEMENT_OPERATION_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "management_operation_invalid");
|
pub const ERROR_CODE_MANAGEMENT_OPERATION_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "management_operation_invalid");
|
||||||
|
|
||||||
/// Error code used when an atomic managed Config or `.env` persistence operation fails before commit.
|
/// Error code used when an atomic managed Config or `.env` persistence operation fails before commit.
|
||||||
pub const ERROR_CODE_PERSISTENCE_WRITE_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "persistence_write_failed");
|
pub const ERROR_CODE_PERSISTENCE_WRITE_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "persistence_write_failed");
|
||||||
|
/// Error code used when an explicitly requested Config profile does not exist in a validated document.
|
||||||
|
pub const ERROR_CODE_PROFILE_NOT_FOUND: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "profile_not_found");
|
||||||
|
/// Error code used when a JSON Schema document is itself invalid for the selected JSON Schema draft.
|
||||||
|
pub const ERROR_CODE_SCHEMA_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "schema_invalid");
|
||||||
|
/// Error code used when a Config document does not satisfy its registered JSON Schema.
|
||||||
|
pub const ERROR_CODE_SCHEMA_VALIDATION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("config", "schema_validation_failed");
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// file: crates/ksp-config-lib/src/lib.rs
|
// file: crates/ksp-config-lib/src/lib.rs
|
||||||
// version: 12
|
// version: 14
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
@@ -25,8 +26,6 @@ mod registry;
|
|||||||
mod sensitivity;
|
mod sensitivity;
|
||||||
mod transport;
|
mod transport;
|
||||||
|
|
||||||
pub(crate) use self::constants::TRACING_TARGET;
|
|
||||||
|
|
||||||
/// Bootstrap argument used to replace the configuration document root.
|
/// Bootstrap argument used to replace the configuration document root.
|
||||||
pub use self::bootstrap::ARG_CFG_PATH;
|
pub use self::bootstrap::ARG_CFG_PATH;
|
||||||
/// Bootstrap argument used to replace the schema root.
|
/// Bootstrap argument used to replace the schema root.
|
||||||
@@ -171,3 +170,22 @@ pub use self::sensitivity::ResolvedConfigJson;
|
|||||||
pub use self::sensitivity::ResolvedConfigText;
|
pub use self::sensitivity::ResolvedConfigText;
|
||||||
/// Effective standard HTTP Transport configuration mapped to `ksp_onchain_transport_lib::HttpTransportSettings`.
|
/// Effective standard HTTP Transport configuration mapped to `ksp_onchain_transport_lib::HttpTransportSettings`.
|
||||||
pub use self::transport::ResolvedTransportConfig;
|
pub use self::transport::ResolvedTransportConfig;
|
||||||
|
|
||||||
|
/// Validates composite document contract.
|
||||||
|
pub(crate) use self::composite::validate_composite_document_contract;
|
||||||
|
/// Owning tracing target for events emitted by the Config crate.
|
||||||
|
pub(crate) use self::constants::TRACING_TARGET;
|
||||||
|
/// Executes the crate-internal parse dotenv content operation for the owning module.
|
||||||
|
pub(crate) use self::environment::parse_dotenv_content;
|
||||||
|
/// Validates supported variable name.
|
||||||
|
pub(crate) use self::environment::validate_supported_variable_name;
|
||||||
|
/// Executes the crate-internal atomic write operation for the owning module.
|
||||||
|
pub(crate) use self::persistence::atomic_write;
|
||||||
|
/// Executes the crate-internal atomic write private operation for the owning module.
|
||||||
|
pub(crate) use self::persistence::atomic_write_private;
|
||||||
|
/// Loads resolved profile with source.
|
||||||
|
pub(crate) use self::profile::load_resolved_profile_with_source;
|
||||||
|
/// Validates document profile contract.
|
||||||
|
pub(crate) use self::profile::validate_document_profile_contract;
|
||||||
|
/// Executes the crate-internal build registry operation for the owning module.
|
||||||
|
pub(crate) use self::registry::build_registry;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-config-lib/src/management.rs
|
// file: crates/ksp-config-lib/src/management.rs
|
||||||
// version: 4
|
// version: 6
|
||||||
|
|
||||||
/// Raw source of one registered Config document read for explicit management/correction.
|
/// Raw source of one registered Config document read for explicit management/correction.
|
||||||
#[derive(Clone, Eq, PartialEq)]
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
@@ -727,7 +727,7 @@ impl ConfigManagement {
|
|||||||
/// Authentication/authorization of the human user belongs to the calling application. Calling this method is the explicit Config boundary that opts into
|
/// Authentication/authorization of the human user belongs to the calling application. Calling this method is the explicit Config boundary that opts into
|
||||||
/// real-value access; the returned value must never be logged.
|
/// real-value access; the returned value must never be logged.
|
||||||
pub fn reveal_effective_environment_value(&self, variable_name: &str) -> ksp_core_lib::Result<std::option::Option<String>> {
|
pub fn reveal_effective_environment_value(&self, variable_name: &str) -> ksp_core_lib::Result<std::option::Option<String>> {
|
||||||
let validation = crate::environment::validate_supported_variable_name(variable_name);
|
let validation = crate::validate_supported_variable_name(variable_name);
|
||||||
if let std::result::Result::Err(error) = validation {
|
if let std::result::Result::Err(error) = validation {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
@@ -744,7 +744,7 @@ impl ConfigManagement {
|
|||||||
|
|
||||||
/// Explicitly reveals the real persisted `.env` value for management display/editing.
|
/// Explicitly reveals the real persisted `.env` value for management display/editing.
|
||||||
pub fn reveal_dotenv_value(&self, variable_name: &str) -> ksp_core_lib::Result<std::option::Option<String>> {
|
pub fn reveal_dotenv_value(&self, variable_name: &str) -> ksp_core_lib::Result<std::option::Option<String>> {
|
||||||
let validation = crate::environment::validate_supported_variable_name(variable_name);
|
let validation = crate::validate_supported_variable_name(variable_name);
|
||||||
if let std::result::Result::Err(error) = validation {
|
if let std::result::Result::Err(error) = validation {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
@@ -758,7 +758,7 @@ impl ConfigManagement {
|
|||||||
|
|
||||||
/// Creates or updates one supported KSP/KSPB `.env` entry atomically without mutating the inherited process environment.
|
/// Creates or updates one supported KSP/KSPB `.env` entry atomically without mutating the inherited process environment.
|
||||||
pub fn set_dotenv_value(&self, variable_name: &str, value: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeReport> {
|
pub fn set_dotenv_value(&self, variable_name: &str, value: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeReport> {
|
||||||
let validation = crate::environment::validate_supported_variable_name(variable_name);
|
let validation = crate::validate_supported_variable_name(variable_name);
|
||||||
if let std::result::Result::Err(error) = validation {
|
if let std::result::Result::Err(error) = validation {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
@@ -781,7 +781,7 @@ impl ConfigManagement {
|
|||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
if candidate != content {
|
if candidate != content {
|
||||||
let write = crate::persistence::atomic_write_private(self.dotenv_path.as_path(), candidate.as_bytes());
|
let write = crate::atomic_write_private(self.dotenv_path.as_path(), candidate.as_bytes());
|
||||||
match write {
|
match write {
|
||||||
std::result::Result::Ok(()) => {},
|
std::result::Result::Ok(()) => {},
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
@@ -797,7 +797,7 @@ impl ConfigManagement {
|
|||||||
|
|
||||||
/// Removes one supported KSP/KSPB `.env` entry atomically without mutating the inherited process environment.
|
/// Removes one supported KSP/KSPB `.env` entry atomically without mutating the inherited process environment.
|
||||||
pub fn remove_dotenv_value(&self, variable_name: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeReport> {
|
pub fn remove_dotenv_value(&self, variable_name: &str) -> ksp_core_lib::Result<ConfigEnvironmentChangeReport> {
|
||||||
let validation = crate::environment::validate_supported_variable_name(variable_name);
|
let validation = crate::validate_supported_variable_name(variable_name);
|
||||||
if let std::result::Result::Err(error) = validation {
|
if let std::result::Result::Err(error) = validation {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
@@ -820,7 +820,7 @@ impl ConfigManagement {
|
|||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
if candidate != content {
|
if candidate != content {
|
||||||
let write = crate::persistence::atomic_write_private(self.dotenv_path.as_path(), candidate.as_bytes());
|
let write = crate::atomic_write_private(self.dotenv_path.as_path(), candidate.as_bytes());
|
||||||
match write {
|
match write {
|
||||||
std::result::Result::Ok(()) => {},
|
std::result::Result::Ok(()) => {},
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
@@ -838,6 +838,7 @@ impl ConfigManagement {
|
|||||||
return crate::ConfigEnvironment::load_from_dotenv_path(self.dotenv_path.as_path());
|
return crate::ConfigEnvironment::load_from_dotenv_path(self.dotenv_path.as_path());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the crate-internal with dotenv path operation for `ConfigManagement`.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn with_dotenv_path(engine: crate::ConfigDocumentEngine, dotenv_path: std::path::PathBuf) -> Self {
|
pub(crate) fn with_dotenv_path(engine: crate::ConfigDocumentEngine, dotenv_path: std::path::PathBuf) -> Self {
|
||||||
return Self { engine, dotenv_path };
|
return Self { engine, dotenv_path };
|
||||||
@@ -921,7 +922,7 @@ fn read_dotenv_source(path: &std::path::Path) -> ksp_core_lib::Result<String> {
|
|||||||
let content = std::fs::read_to_string(path);
|
let content = std::fs::read_to_string(path);
|
||||||
return match content {
|
return match content {
|
||||||
std::result::Result::Ok(value) => {
|
std::result::Result::Ok(value) => {
|
||||||
let validation = crate::environment::parse_dotenv_content(path, value.as_str());
|
let validation = crate::parse_dotenv_content(path, value.as_str());
|
||||||
match validation {
|
match validation {
|
||||||
std::result::Result::Ok(_) => std::result::Result::Ok(value),
|
std::result::Result::Ok(_) => std::result::Result::Ok(value),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||||
@@ -937,7 +938,7 @@ fn read_dotenv_source(path: &std::path::Path) -> ksp_core_lib::Result<String> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_dotenv_source(path: &std::path::Path, source: &str, variable_name: &str, replacement: std::option::Option<&str>) -> ksp_core_lib::Result<String> {
|
fn update_dotenv_source(path: &std::path::Path, source: &str, variable_name: &str, replacement: std::option::Option<&str>) -> ksp_core_lib::Result<String> {
|
||||||
let validation = crate::environment::parse_dotenv_content(path, source);
|
let validation = crate::parse_dotenv_content(path, source);
|
||||||
if let std::result::Result::Err(error) = validation {
|
if let std::result::Result::Err(error) = validation {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
@@ -967,7 +968,7 @@ fn update_dotenv_source(path: &std::path::Path, source: &str, variable_name: &st
|
|||||||
if !candidate.is_empty() {
|
if !candidate.is_empty() {
|
||||||
candidate.push('\n');
|
candidate.push('\n');
|
||||||
}
|
}
|
||||||
let candidate_validation = crate::environment::parse_dotenv_content(path, candidate.as_str());
|
let candidate_validation = crate::parse_dotenv_content(path, candidate.as_str());
|
||||||
return match candidate_validation {
|
return match candidate_validation {
|
||||||
std::result::Result::Ok(_) => std::result::Result::Ok(candidate),
|
std::result::Result::Ok(_) => std::result::Result::Ok(candidate),
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||||
@@ -1030,7 +1031,7 @@ fn persist_document_source(file_id: &crate::ConfigFileId, path: &std::path::Path
|
|||||||
if existing.as_slice() == source {
|
if existing.as_slice() == source {
|
||||||
return std::result::Result::Ok(ConfigDocumentChangeReport { source_changed: false, reload_required: false });
|
return std::result::Result::Ok(ConfigDocumentChangeReport { source_changed: false, reload_required: false });
|
||||||
}
|
}
|
||||||
let write = crate::persistence::atomic_write(path, source);
|
let write = crate::atomic_write(path, source);
|
||||||
if let std::result::Result::Err(error) = write {
|
if let std::result::Result::Err(error) = write {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
// file: crates/ksp-config-lib/src/persistence.rs
|
// file: crates/ksp-config-lib/src/persistence.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
static NEXT_TEMPORARY_FILE_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
|
static NEXT_TEMPORARY_FILE_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(1);
|
||||||
|
|
||||||
|
/// Executes the crate-internal atomic write operation for the owning module.
|
||||||
pub(crate) fn atomic_write(path: &std::path::Path, content: &[u8]) -> ksp_core_lib::Result<()> {
|
pub(crate) fn atomic_write(path: &std::path::Path, content: &[u8]) -> ksp_core_lib::Result<()> {
|
||||||
return atomic_write_with_policy(path, content, false);
|
return atomic_write_with_policy(path, content, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the crate-internal atomic write private operation for the owning module.
|
||||||
pub(crate) fn atomic_write_private(path: &std::path::Path, content: &[u8]) -> ksp_core_lib::Result<()> {
|
pub(crate) fn atomic_write_private(path: &std::path::Path, content: &[u8]) -> ksp_core_lib::Result<()> {
|
||||||
return atomic_write_with_policy(path, content, true);
|
return atomic_write_with_policy(path, content, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-config-lib/src/profile.rs
|
// file: crates/ksp-config-lib/src/profile.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
/// Origin of one top-level value in a resolved standard Config profile.
|
/// Origin of one top-level value in a resolved standard Config profile.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
@@ -135,6 +135,7 @@ impl crate::ConfigDocumentEngine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Loads resolved profile with source.
|
||||||
pub(crate) fn load_resolved_profile_with_source(
|
pub(crate) fn load_resolved_profile_with_source(
|
||||||
engine: &crate::ConfigDocumentEngine,
|
engine: &crate::ConfigDocumentEngine,
|
||||||
file_id: &crate::ConfigFileId,
|
file_id: &crate::ConfigFileId,
|
||||||
@@ -153,6 +154,7 @@ pub(crate) fn load_resolved_profile_with_source(
|
|||||||
return resolve_document_profile(&document, requested_profile, source);
|
return resolve_document_profile(&document, requested_profile, source);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Validates document profile contract.
|
||||||
pub(crate) fn validate_document_profile_contract(document: &crate::ConfigJsonDocument) -> ksp_core_lib::Result<()> {
|
pub(crate) fn validate_document_profile_contract(document: &crate::ConfigJsonDocument) -> ksp_core_lib::Result<()> {
|
||||||
let root = match document.value().as_object() {
|
let root = match document.value().as_object() {
|
||||||
std::option::Option::Some(value) => value,
|
std::option::Option::Some(value) => value,
|
||||||
|
|||||||
@@ -1,18 +1,10 @@
|
|||||||
// file: crates/ksp-config-lib/src/registry.rs
|
// file: crates/ksp-config-lib/src/registry.rs
|
||||||
// version: 5
|
// version: 7
|
||||||
|
|
||||||
/// Bootstrap argument used to replace a known Config filename mapping.
|
/// Bootstrap argument used to replace a known Config filename mapping.
|
||||||
pub const ARG_FILE_MAP: &str = "--filemap";
|
pub const ARG_FILE_MAP: &str = "--filemap";
|
||||||
/// Logical file identifier for the standard Logging configuration document.
|
/// Default physical filename for the generic composite JSON Schema document.
|
||||||
pub const FILE_ID_STD_LOGGING: &str = "cfg.std.logging";
|
pub const DEFAULT_COMPOSITE_SCHEMA_FILENAME: &str = "composite.schema.json";
|
||||||
/// Logical file identifier for the standard Logging JSON Schema document.
|
|
||||||
pub const FILE_ID_SCHEMA_STD_LOGGING: &str = "schema.std.logging";
|
|
||||||
/// Logical file identifier for the standard HTTP Transport configuration document.
|
|
||||||
pub const FILE_ID_STD_TRANSPORT: &str = "cfg.std.transport";
|
|
||||||
/// Logical file identifier for the standard HTTP Transport JSON Schema document.
|
|
||||||
pub const FILE_ID_SCHEMA_STD_TRANSPORT: &str = "schema.std.transport";
|
|
||||||
/// Logical file identifier for the generic composite JSON Schema document.
|
|
||||||
pub const FILE_ID_SCHEMA_COMPOSITE: &str = "schema.composite";
|
|
||||||
/// Default physical filename for the standard Logging configuration document.
|
/// Default physical filename for the standard Logging configuration document.
|
||||||
pub const DEFAULT_STD_LOGGING_FILENAME: &str = "std.logging.json";
|
pub const DEFAULT_STD_LOGGING_FILENAME: &str = "std.logging.json";
|
||||||
/// Default physical filename for the standard Logging JSON Schema document.
|
/// Default physical filename for the standard Logging JSON Schema document.
|
||||||
@@ -21,8 +13,16 @@ pub const DEFAULT_STD_LOGGING_SCHEMA_FILENAME: &str = "std.logging.schema.json";
|
|||||||
pub const DEFAULT_STD_TRANSPORT_FILENAME: &str = "std.transport.json";
|
pub const DEFAULT_STD_TRANSPORT_FILENAME: &str = "std.transport.json";
|
||||||
/// Default physical filename for the standard HTTP Transport JSON Schema document.
|
/// Default physical filename for the standard HTTP Transport JSON Schema document.
|
||||||
pub const DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME: &str = "std.transport.schema.json";
|
pub const DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME: &str = "std.transport.schema.json";
|
||||||
/// Default physical filename for the generic composite JSON Schema document.
|
/// Logical file identifier for the generic composite JSON Schema document.
|
||||||
pub const DEFAULT_COMPOSITE_SCHEMA_FILENAME: &str = "composite.schema.json";
|
pub const FILE_ID_SCHEMA_COMPOSITE: &str = "schema.composite";
|
||||||
|
/// Logical file identifier for the standard Logging JSON Schema document.
|
||||||
|
pub const FILE_ID_SCHEMA_STD_LOGGING: &str = "schema.std.logging";
|
||||||
|
/// Logical file identifier for the standard HTTP Transport JSON Schema document.
|
||||||
|
pub const FILE_ID_SCHEMA_STD_TRANSPORT: &str = "schema.std.transport";
|
||||||
|
/// Logical file identifier for the standard Logging configuration document.
|
||||||
|
pub const FILE_ID_STD_LOGGING: &str = "cfg.std.logging";
|
||||||
|
/// Logical file identifier for the standard HTTP Transport configuration document.
|
||||||
|
pub const FILE_ID_STD_TRANSPORT: &str = "cfg.std.transport";
|
||||||
|
|
||||||
/// Stable logical identifier for a Config-managed file.
|
/// Stable logical identifier for a Config-managed file.
|
||||||
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
||||||
@@ -89,6 +89,7 @@ impl ConfigFileDescriptor {
|
|||||||
return self.schema_file_id.as_ref();
|
return self.schema_file_id.as_ref();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a new `ConfigFileDescriptor` value.
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
file_id: &'static str,
|
file_id: &'static str,
|
||||||
kind: ConfigFileKind,
|
kind: ConfigFileKind,
|
||||||
@@ -165,7 +166,7 @@ impl ConfigFileRegistry {
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
return build_registry([logging, logging_schema, transport, transport_schema, composite_schema]);
|
return crate::build_registry([logging, logging_schema, transport, transport_schema, composite_schema]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates the default registry and applies repeatable `--filemap=<file_id>=<filename>` overrides from raw process arguments.
|
/// Creates the default registry and applies repeatable `--filemap=<file_id>=<filename>` overrides from raw process arguments.
|
||||||
@@ -249,6 +250,7 @@ impl ConfigFileRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the crate-internal build registry operation for the owning module.
|
||||||
pub(crate) fn build_registry<const N: usize>(descriptors: [ConfigFileDescriptor; N]) -> ksp_core_lib::Result<ConfigFileRegistry> {
|
pub(crate) fn build_registry<const N: usize>(descriptors: [ConfigFileDescriptor; N]) -> ksp_core_lib::Result<ConfigFileRegistry> {
|
||||||
let mut registry = ConfigFileRegistry { descriptors: std::collections::BTreeMap::new() };
|
let mut registry = ConfigFileRegistry { descriptors: std::collections::BTreeMap::new() };
|
||||||
for descriptor in descriptors {
|
for descriptor in descriptors {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-config-lib/src/sensitivity.rs
|
// file: crates/ksp-config-lib/src/sensitivity.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// Replacement used for secret environment fragments in safe diagnostic representations.
|
/// Replacement used for secret environment fragments in safe diagnostic representations.
|
||||||
pub const REDACTED_CONFIG_VALUE: &str = "********";
|
pub const REDACTED_CONFIG_VALUE: &str = "********";
|
||||||
@@ -18,7 +18,7 @@ pub enum ConfigSensitivity {
|
|||||||
impl ConfigSensitivity {
|
impl ConfigSensitivity {
|
||||||
/// Classifies one supported KSP/KSPB environment variable by its namespace.
|
/// Classifies one supported KSP/KSPB environment variable by its namespace.
|
||||||
pub fn from_variable_name(variable_name: &str) -> ksp_core_lib::Result<Self> {
|
pub fn from_variable_name(variable_name: &str) -> ksp_core_lib::Result<Self> {
|
||||||
let validation = crate::environment::validate_supported_variable_name(variable_name);
|
let validation = crate::validate_supported_variable_name(variable_name);
|
||||||
if let std::result::Result::Err(error) = validation {
|
if let std::result::Result::Err(error) = validation {
|
||||||
return std::result::Result::Err(error);
|
return std::result::Result::Err(error);
|
||||||
}
|
}
|
||||||
@@ -103,6 +103,7 @@ pub struct ResolvedConfigText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResolvedConfigText {
|
impl ResolvedConfigText {
|
||||||
|
/// Creates a new `ResolvedConfigText` value.
|
||||||
pub(crate) fn new(value: String, safe_value: String, sensitivity: ConfigSensitivity, provenance: std::vec::Vec<ConfigValueProvenance>) -> Self {
|
pub(crate) fn new(value: String, safe_value: String, sensitivity: ConfigSensitivity, provenance: std::vec::Vec<ConfigValueProvenance>) -> Self {
|
||||||
return Self { value, safe_value, sensitivity, provenance };
|
return Self { value, safe_value, sensitivity, provenance };
|
||||||
}
|
}
|
||||||
@@ -153,6 +154,7 @@ pub struct ResolvedConfigJson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ResolvedConfigJson {
|
impl ResolvedConfigJson {
|
||||||
|
/// Creates a new `ResolvedConfigJson` value.
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
value: serde_json::Value,
|
value: serde_json::Value,
|
||||||
safe_value: serde_json::Value,
|
safe_value: serde_json::Value,
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
// file: crates/ksp-config-lib/unit_tests/bootstrap.rs
|
// file: crates/ksp-config-lib/unit_tests/bootstrap.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn defaults_use_hardcoded_ksp_roots() {
|
fn defaults_use_hardcoded_ksp_roots() {
|
||||||
let result = super::ConfigBootstrapOptions::defaults();
|
let result = crate::ConfigBootstrapOptions::defaults();
|
||||||
assert!(result.is_ok(), "default bootstrap paths should be valid: {result:?}");
|
assert!(result.is_ok(), "default bootstrap paths should be valid: {result:?}");
|
||||||
if let std::result::Result::Ok(options) = result {
|
if let std::result::Result::Ok(options) = result {
|
||||||
assert_eq!(options.cfg_path(), std::path::Path::new(super::DEFAULT_CFG_PATH));
|
assert_eq!(options.cfg_path(), std::path::Path::new(crate::DEFAULT_CFG_PATH));
|
||||||
assert_eq!(options.schema_path(), std::path::Path::new(super::DEFAULT_SCHEMA_PATH));
|
assert_eq!(options.schema_path(), std::path::Path::new(crate::DEFAULT_SCHEMA_PATH));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn cfg_path_override_keeps_schema_default() {
|
fn cfg_path_override_keeps_schema_default() {
|
||||||
let defaults = super::ConfigBootstrapOptions::defaults();
|
let defaults = crate::ConfigBootstrapOptions::defaults();
|
||||||
assert!(defaults.is_ok(), "default bootstrap paths should be valid: {defaults:?}");
|
assert!(defaults.is_ok(), "default bootstrap paths should be valid: {defaults:?}");
|
||||||
if let std::result::Result::Ok(options) = defaults {
|
if let std::result::Result::Ok(options) = defaults {
|
||||||
let result = options.with_cfg_path("custom-config");
|
let result = options.with_cfg_path("custom-config");
|
||||||
assert!(result.is_ok(), "cfg path override should be valid: {result:?}");
|
assert!(result.is_ok(), "cfg path override should be valid: {result:?}");
|
||||||
if let std::result::Result::Ok(options) = result {
|
if let std::result::Result::Ok(options) = result {
|
||||||
assert_eq!(options.cfg_path(), std::path::Path::new("custom-config"));
|
assert_eq!(options.cfg_path(), std::path::Path::new("custom-config"));
|
||||||
assert_eq!(options.schema_path(), std::path::Path::new(super::DEFAULT_SCHEMA_PATH));
|
assert_eq!(options.schema_path(), std::path::Path::new(crate::DEFAULT_SCHEMA_PATH));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn schema_path_override_keeps_cfg_default() {
|
fn schema_path_override_keeps_cfg_default() {
|
||||||
let defaults = super::ConfigBootstrapOptions::defaults();
|
let defaults = crate::ConfigBootstrapOptions::defaults();
|
||||||
assert!(defaults.is_ok(), "default bootstrap paths should be valid: {defaults:?}");
|
assert!(defaults.is_ok(), "default bootstrap paths should be valid: {defaults:?}");
|
||||||
if let std::result::Result::Ok(options) = defaults {
|
if let std::result::Result::Ok(options) = defaults {
|
||||||
let result = options.with_schema_path("custom-schemas");
|
let result = options.with_schema_path("custom-schemas");
|
||||||
assert!(result.is_ok(), "schema path override should be valid: {result:?}");
|
assert!(result.is_ok(), "schema path override should be valid: {result:?}");
|
||||||
if let std::result::Result::Ok(options) = result {
|
if let std::result::Result::Ok(options) = result {
|
||||||
assert_eq!(options.cfg_path(), std::path::Path::new(super::DEFAULT_CFG_PATH));
|
assert_eq!(options.cfg_path(), std::path::Path::new(crate::DEFAULT_CFG_PATH));
|
||||||
assert_eq!(options.schema_path(), std::path::Path::new("custom-schemas"));
|
assert_eq!(options.schema_path(), std::path::Path::new("custom-schemas"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,21 +42,21 @@ fn schema_path_override_keeps_cfg_default() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn cfg_cli_override_keeps_schema_default() {
|
fn cfg_cli_override_keeps_schema_default() {
|
||||||
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--cfgpath=cli-config")];
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--cfgpath=cli-config")];
|
||||||
let result = super::ConfigBootstrapOptions::from_args(&args);
|
let result = crate::ConfigBootstrapOptions::from_args(&args);
|
||||||
assert!(result.is_ok(), "cfg CLI override should parse: {result:?}");
|
assert!(result.is_ok(), "cfg CLI override should parse: {result:?}");
|
||||||
if let std::result::Result::Ok(options) = result {
|
if let std::result::Result::Ok(options) = result {
|
||||||
assert_eq!(options.cfg_path(), std::path::Path::new("cli-config"));
|
assert_eq!(options.cfg_path(), std::path::Path::new("cli-config"));
|
||||||
assert_eq!(options.schema_path(), std::path::Path::new(super::DEFAULT_SCHEMA_PATH));
|
assert_eq!(options.schema_path(), std::path::Path::new(crate::DEFAULT_SCHEMA_PATH));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn schema_cli_override_keeps_cfg_default() {
|
fn schema_cli_override_keeps_cfg_default() {
|
||||||
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--schemapath=cli-schemas")];
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--schemapath=cli-schemas")];
|
||||||
let result = super::ConfigBootstrapOptions::from_args(&args);
|
let result = crate::ConfigBootstrapOptions::from_args(&args);
|
||||||
assert!(result.is_ok(), "schema CLI override should parse: {result:?}");
|
assert!(result.is_ok(), "schema CLI override should parse: {result:?}");
|
||||||
if let std::result::Result::Ok(options) = result {
|
if let std::result::Result::Ok(options) = result {
|
||||||
assert_eq!(options.cfg_path(), std::path::Path::new(super::DEFAULT_CFG_PATH));
|
assert_eq!(options.cfg_path(), std::path::Path::new(crate::DEFAULT_CFG_PATH));
|
||||||
assert_eq!(options.schema_path(), std::path::Path::new("cli-schemas"));
|
assert_eq!(options.schema_path(), std::path::Path::new("cli-schemas"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -73,7 +73,7 @@ fn parser_accepts_inline_and_separate_forms_and_last_value_wins() {
|
|||||||
std::ffi::OsString::from("--schemapath"),
|
std::ffi::OsString::from("--schemapath"),
|
||||||
std::ffi::OsString::from("second-schemas"),
|
std::ffi::OsString::from("second-schemas"),
|
||||||
];
|
];
|
||||||
let result = super::ConfigBootstrapOptions::from_args(&args);
|
let result = crate::ConfigBootstrapOptions::from_args(&args);
|
||||||
assert!(result.is_ok(), "bootstrap arguments should parse: {result:?}");
|
assert!(result.is_ok(), "bootstrap arguments should parse: {result:?}");
|
||||||
if let std::result::Result::Ok(options) = result {
|
if let std::result::Result::Ok(options) = result {
|
||||||
assert_eq!(options.cfg_path(), std::path::Path::new("second-config"));
|
assert_eq!(options.cfg_path(), std::path::Path::new("second-config"));
|
||||||
@@ -84,7 +84,7 @@ fn parser_accepts_inline_and_separate_forms_and_last_value_wins() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parser_reports_missing_separate_value() {
|
fn parser_reports_missing_separate_value() {
|
||||||
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--cfgpath")];
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--cfgpath")];
|
||||||
let result = super::ConfigBootstrapOptions::from_args(&args);
|
let result = crate::ConfigBootstrapOptions::from_args(&args);
|
||||||
assert!(result.is_err(), "missing value must be rejected");
|
assert!(result.is_err(), "missing value must be rejected");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_BOOTSTRAP_ARGUMENT_MISSING_VALUE);
|
assert_eq!(error.code(), crate::ERROR_CODE_BOOTSTRAP_ARGUMENT_MISSING_VALUE);
|
||||||
@@ -94,7 +94,7 @@ fn parser_reports_missing_separate_value() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parser_reports_another_option_as_missing_separate_value() {
|
fn parser_reports_another_option_as_missing_separate_value() {
|
||||||
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--cfgpath"), std::ffi::OsString::from("--other-option")];
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--cfgpath"), std::ffi::OsString::from("--other-option")];
|
||||||
let result = super::ConfigBootstrapOptions::from_args(&args);
|
let result = crate::ConfigBootstrapOptions::from_args(&args);
|
||||||
assert!(result.is_err(), "another option must not become a bootstrap path value");
|
assert!(result.is_err(), "another option must not become a bootstrap path value");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_BOOTSTRAP_ARGUMENT_MISSING_VALUE);
|
assert_eq!(error.code(), crate::ERROR_CODE_BOOTSTRAP_ARGUMENT_MISSING_VALUE);
|
||||||
@@ -104,7 +104,7 @@ fn parser_reports_another_option_as_missing_separate_value() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn empty_inline_path_is_rejected() {
|
fn empty_inline_path_is_rejected() {
|
||||||
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--schemapath=")];
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--schemapath=")];
|
||||||
let result = super::ConfigBootstrapOptions::from_args(&args);
|
let result = crate::ConfigBootstrapOptions::from_args(&args);
|
||||||
assert!(result.is_err(), "empty path must be rejected");
|
assert!(result.is_err(), "empty path must be rejected");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_BOOTSTRAP_INVALID_PATH);
|
assert_eq!(error.code(), crate::ERROR_CODE_BOOTSTRAP_INVALID_PATH);
|
||||||
@@ -113,7 +113,7 @@ fn empty_inline_path_is_rejected() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn explicit_programmatic_paths_do_not_depend_on_default_roots() {
|
fn explicit_programmatic_paths_do_not_depend_on_default_roots() {
|
||||||
let result = super::ConfigBootstrapOptions::from_paths("programmatic-config", "programmatic-schemas");
|
let result = crate::ConfigBootstrapOptions::from_paths("programmatic-config", "programmatic-schemas");
|
||||||
assert!(result.is_ok(), "explicit programmatic paths should be valid: {result:?}");
|
assert!(result.is_ok(), "explicit programmatic paths should be valid: {result:?}");
|
||||||
if let std::result::Result::Ok(options) = result {
|
if let std::result::Result::Ok(options) = result {
|
||||||
assert_eq!(options.cfg_path(), std::path::Path::new("programmatic-config"));
|
assert_eq!(options.cfg_path(), std::path::Path::new("programmatic-config"));
|
||||||
@@ -126,7 +126,7 @@ fn existing_non_directory_path_is_rejected() {
|
|||||||
let fixture = unique_fixture_path("existing-file");
|
let fixture = unique_fixture_path("existing-file");
|
||||||
let create = std::fs::write(fixture.as_path(), b"fixture");
|
let create = std::fs::write(fixture.as_path(), b"fixture");
|
||||||
assert!(create.is_ok(), "fixture file should be creatable: {create:?}");
|
assert!(create.is_ok(), "fixture file should be creatable: {create:?}");
|
||||||
let result = super::ConfigBootstrapOptions::from_paths(fixture.as_path(), "programmatic-schemas");
|
let result = crate::ConfigBootstrapOptions::from_paths(fixture.as_path(), "programmatic-schemas");
|
||||||
let remove = std::fs::remove_file(fixture.as_path());
|
let remove = std::fs::remove_file(fixture.as_path());
|
||||||
assert!(remove.is_ok(), "fixture file should be removable: {remove:?}");
|
assert!(remove.is_ok(), "fixture file should be removable: {remove:?}");
|
||||||
assert!(result.is_err(), "existing file must not be accepted as a bootstrap directory");
|
assert!(result.is_err(), "existing file must not be accepted as a bootstrap directory");
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
// file: crates/ksp-config-lib/unit_tests/composite.rs
|
// file: crates/ksp-config-lib/unit_tests/composite.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
const TEST_COMPOSITE_FILE_ID: &str = "cfg.composite.test";
|
|
||||||
const TEST_COMPOSITE_FILENAME: &str = "examples/composite.example.json";
|
const TEST_COMPOSITE_FILENAME: &str = "examples/composite.example.json";
|
||||||
|
const TEST_COMPOSITE_FILE_ID: &str = "cfg.composite.test";
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fixture_composite_example_resolves_default_document_profile() {
|
fn fixture_composite_example_resolves_default_document_profile() {
|
||||||
@@ -197,7 +197,7 @@ fn test_registry(composite_filename: &'static str) -> ksp_core_lib::Result<crate
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
return crate::registry::build_registry([logging, logging_schema, composite_schema, composite]);
|
return crate::build_registry([logging, logging_schema, composite_schema, composite]);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_fixture(fixture: &FixtureRoots, composite: &str) -> std::io::Result<()> {
|
fn prepare_fixture(fixture: &FixtureRoots, composite: &str) -> std::io::Result<()> {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-config-lib/unit_tests/document.rs
|
// file: crates/ksp-config-lib/unit_tests/document.rs
|
||||||
// version: 3
|
// version: 4
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn committed_logging_document_passes_registered_schema_and_semantic_validation() {
|
fn committed_logging_document_passes_registered_schema_and_semantic_validation() {
|
||||||
@@ -11,7 +11,7 @@ fn committed_logging_document_passes_registered_schema_and_semantic_validation()
|
|||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
assert!(file_id.is_ok(), "logging file_id should be valid: {file_id:?}");
|
assert!(file_id.is_ok(), "logging file_id should be valid: {file_id:?}");
|
||||||
if let (std::result::Result::Ok(bootstrap), std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (bootstrap, registry, file_id) {
|
if let (std::result::Result::Ok(bootstrap), std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (bootstrap, registry, file_id) {
|
||||||
let engine = super::ConfigDocumentEngine::new(bootstrap, registry);
|
let engine = crate::ConfigDocumentEngine::new(bootstrap, registry);
|
||||||
let document = engine.load_validated_document(&file_id);
|
let document = engine.load_validated_document(&file_id);
|
||||||
assert!(document.is_ok(), "committed std.logging.json should validate: {document:?}");
|
assert!(document.is_ok(), "committed std.logging.json should validate: {document:?}");
|
||||||
if let std::result::Result::Ok(document) = document {
|
if let std::result::Result::Ok(document) = document {
|
||||||
@@ -228,7 +228,7 @@ fn valid_logging_profile(profile_id: &str, output_id: &str) -> String {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_fixture(fixture: &FixtureRoots) -> ksp_core_lib::Result<super::ConfigJsonDocument> {
|
fn load_fixture(fixture: &FixtureRoots) -> ksp_core_lib::Result<crate::ConfigJsonDocument> {
|
||||||
let bootstrap = crate::ConfigBootstrapOptions::from_paths(fixture.config.as_path(), fixture.schemas.as_path());
|
let bootstrap = crate::ConfigBootstrapOptions::from_paths(fixture.config.as_path(), fixture.schemas.as_path());
|
||||||
let bootstrap = match bootstrap {
|
let bootstrap = match bootstrap {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -244,7 +244,7 @@ fn load_fixture(fixture: &FixtureRoots) -> ksp_core_lib::Result<super::ConfigJso
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||||
};
|
};
|
||||||
let engine = super::ConfigDocumentEngine::new(bootstrap, registry);
|
let engine = crate::ConfigDocumentEngine::new(bootstrap, registry);
|
||||||
return engine.load_validated_document(&file_id);
|
return engine.load_validated_document(&file_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,7 +278,7 @@ fn valid_minimal_logging_schema() -> &'static str {
|
|||||||
}"#;
|
}"#;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_error_code(result: ksp_core_lib::Result<super::ConfigJsonDocument>, expected: ksp_core_lib::ErrorCode) {
|
fn assert_error_code(result: ksp_core_lib::Result<crate::ConfigJsonDocument>, expected: ksp_core_lib::ErrorCode) {
|
||||||
assert!(result.is_err(), "fixture should fail with {expected:?}: {result:?}");
|
assert!(result.is_err(), "fixture should fail with {expected:?}: {result:?}");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), expected);
|
assert_eq!(error.code(), expected);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-config-lib/unit_tests/environment.rs
|
// file: crates/ksp-config-lib/unit_tests/environment.rs
|
||||||
// version: 5
|
// version: 6
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn process_environment_wins_over_dotenv_and_fallback_even_when_empty() {
|
fn process_environment_wins_over_dotenv_and_fallback_even_when_empty() {
|
||||||
@@ -7,7 +7,7 @@ fn process_environment_wins_over_dotenv_and_fallback_even_when_empty() {
|
|||||||
process.insert("KSP_LOGS_DIRECTORY".to_owned(), String::new());
|
process.insert("KSP_LOGS_DIRECTORY".to_owned(), String::new());
|
||||||
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
||||||
dotenv.insert("KSP_LOGS_DIRECTORY".to_owned(), "dotenv-logs".to_owned());
|
dotenv.insert("KSP_LOGS_DIRECTORY".to_owned(), "dotenv-logs".to_owned());
|
||||||
let environment = super::ConfigEnvironment::from_maps(process, dotenv);
|
let environment = crate::ConfigEnvironment::from_maps(process, dotenv);
|
||||||
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::Some("fallback-logs"));
|
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::Some("fallback-logs"));
|
||||||
assert!(resolved.is_ok(), "process value should resolve");
|
assert!(resolved.is_ok(), "process value should resolve");
|
||||||
let resolved = match resolved {
|
let resolved = match resolved {
|
||||||
@@ -15,7 +15,7 @@ fn process_environment_wins_over_dotenv_and_fallback_even_when_empty() {
|
|||||||
std::result::Result::Err(_) => return,
|
std::result::Result::Err(_) => return,
|
||||||
};
|
};
|
||||||
assert_eq!(resolved.value(), "");
|
assert_eq!(resolved.value(), "");
|
||||||
assert_eq!(resolved.source(), super::ConfigEnvironmentSource::Process);
|
assert_eq!(resolved.source(), crate::ConfigEnvironmentSource::Process);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -23,7 +23,7 @@ fn dotenv_wins_over_fallback_when_process_value_is_absent() {
|
|||||||
let process = std::collections::BTreeMap::<String, String>::new();
|
let process = std::collections::BTreeMap::<String, String>::new();
|
||||||
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
||||||
dotenv.insert("KSP_LOGS_DIRECTORY".to_owned(), "dotenv-logs".to_owned());
|
dotenv.insert("KSP_LOGS_DIRECTORY".to_owned(), "dotenv-logs".to_owned());
|
||||||
let environment = super::ConfigEnvironment::from_maps(process, dotenv);
|
let environment = crate::ConfigEnvironment::from_maps(process, dotenv);
|
||||||
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::Some("fallback-logs"));
|
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::Some("fallback-logs"));
|
||||||
assert!(resolved.is_ok(), "dotenv value should resolve");
|
assert!(resolved.is_ok(), "dotenv value should resolve");
|
||||||
let resolved = match resolved {
|
let resolved = match resolved {
|
||||||
@@ -31,25 +31,25 @@ fn dotenv_wins_over_fallback_when_process_value_is_absent() {
|
|||||||
std::result::Result::Err(_) => return,
|
std::result::Result::Err(_) => return,
|
||||||
};
|
};
|
||||||
assert_eq!(resolved.value(), "dotenv-logs");
|
assert_eq!(resolved.value(), "dotenv-logs");
|
||||||
assert_eq!(resolved.source(), super::ConfigEnvironmentSource::DotEnv);
|
assert_eq!(resolved.source(), crate::ConfigEnvironmentSource::DotEnv);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn empty_dotenv_value_is_defined_and_beats_fallback() {
|
fn empty_dotenv_value_is_defined_and_beats_fallback() {
|
||||||
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
||||||
dotenv.insert("KSP_LOGS_DIRECTORY".to_owned(), String::new());
|
dotenv.insert("KSP_LOGS_DIRECTORY".to_owned(), String::new());
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), dotenv);
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), dotenv);
|
||||||
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::Some("fallback-logs"));
|
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::Some("fallback-logs"));
|
||||||
assert!(resolved.is_ok(), "empty dotenv value should resolve");
|
assert!(resolved.is_ok(), "empty dotenv value should resolve");
|
||||||
if let std::result::Result::Ok(resolved) = resolved {
|
if let std::result::Result::Ok(resolved) = resolved {
|
||||||
assert_eq!(resolved.value(), "");
|
assert_eq!(resolved.value(), "");
|
||||||
assert_eq!(resolved.source(), super::ConfigEnvironmentSource::DotEnv);
|
assert_eq!(resolved.source(), crate::ConfigEnvironmentSource::DotEnv);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fallback_is_used_only_when_external_sources_are_absent() {
|
fn fallback_is_used_only_when_external_sources_are_absent() {
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||||
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::Some("fallback-logs"));
|
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::Some("fallback-logs"));
|
||||||
assert!(resolved.is_ok(), "fallback should resolve");
|
assert!(resolved.is_ok(), "fallback should resolve");
|
||||||
let resolved = match resolved {
|
let resolved = match resolved {
|
||||||
@@ -57,12 +57,12 @@ fn fallback_is_used_only_when_external_sources_are_absent() {
|
|||||||
std::result::Result::Err(_) => return,
|
std::result::Result::Err(_) => return,
|
||||||
};
|
};
|
||||||
assert_eq!(resolved.value(), "fallback-logs");
|
assert_eq!(resolved.value(), "fallback-logs");
|
||||||
assert_eq!(resolved.source(), super::ConfigEnvironmentSource::Fallback);
|
assert_eq!(resolved.source(), crate::ConfigEnvironmentSource::Fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_variable_without_fallback_is_a_distinct_error() {
|
fn missing_variable_without_fallback_is_a_distinct_error() {
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||||
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::None);
|
let resolved = environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::None);
|
||||||
let error = match resolved {
|
let error = match resolved {
|
||||||
std::result::Result::Ok(_) => return,
|
std::result::Result::Ok(_) => return,
|
||||||
@@ -78,7 +78,7 @@ fn ksp_and_kspb_namespaces_are_supported_but_external_names_are_rejected() {
|
|||||||
process.insert("KSP_LOGS_DIRECTORY".to_owned(), "logs".to_owned());
|
process.insert("KSP_LOGS_DIRECTORY".to_owned(), "logs".to_owned());
|
||||||
let bot_variable = ["KSPB_", "TEST_KEY"].concat();
|
let bot_variable = ["KSPB_", "TEST_KEY"].concat();
|
||||||
process.insert(bot_variable.clone(), "hidden".to_owned());
|
process.insert(bot_variable.clone(), "hidden".to_owned());
|
||||||
let environment = super::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
|
||||||
assert!(environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::None).is_ok());
|
assert!(environment.resolve_variable("KSP_LOGS_DIRECTORY", std::option::Option::None).is_ok());
|
||||||
assert!(environment.resolve_variable(bot_variable.as_str(), std::option::Option::None).is_ok());
|
assert!(environment.resolve_variable(bot_variable.as_str(), std::option::Option::None).is_ok());
|
||||||
let external = environment.resolve_variable("OTHER_NETWORK", std::option::Option::None);
|
let external = environment.resolve_variable("OTHER_NETWORK", std::option::Option::None);
|
||||||
@@ -91,7 +91,7 @@ fn ksp_and_kspb_namespaces_are_supported_but_external_names_are_rejected() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn text_resolver_supports_multiple_placeholders_and_literal_fallbacks() {
|
fn text_resolver_supports_multiple_placeholders_and_literal_fallbacks() {
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||||
let resolved = environment.resolve_text("logs=${KSP_LOGS_DIRECTORY:-logs};second=${KSP_LOGS_DIRECTORY:-other}");
|
let resolved = environment.resolve_text("logs=${KSP_LOGS_DIRECTORY:-logs};second=${KSP_LOGS_DIRECTORY:-other}");
|
||||||
assert!(resolved.is_ok(), "multiple placeholders should resolve");
|
assert!(resolved.is_ok(), "multiple placeholders should resolve");
|
||||||
if let std::result::Result::Ok(resolved) = resolved {
|
if let std::result::Result::Ok(resolved) = resolved {
|
||||||
@@ -101,7 +101,7 @@ fn text_resolver_supports_multiple_placeholders_and_literal_fallbacks() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn malformed_or_nested_placeholders_are_rejected() {
|
fn malformed_or_nested_placeholders_are_rejected() {
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||||
let unclosed = environment.resolve_text("${KSP_LOGS_DIRECTORY");
|
let unclosed = environment.resolve_text("${KSP_LOGS_DIRECTORY");
|
||||||
let unclosed = match unclosed {
|
let unclosed = match unclosed {
|
||||||
std::result::Result::Ok(_) => return,
|
std::result::Result::Ok(_) => return,
|
||||||
@@ -120,7 +120,7 @@ fn malformed_or_nested_placeholders_are_rejected() {
|
|||||||
fn json_resolver_walks_objects_and_arrays_without_changing_keys() {
|
fn json_resolver_walks_objects_and_arrays_without_changing_keys() {
|
||||||
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
||||||
dotenv.insert("KSP_LOGS_DIRECTORY".to_owned(), "runtime-logs".to_owned());
|
dotenv.insert("KSP_LOGS_DIRECTORY".to_owned(), "runtime-logs".to_owned());
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), dotenv);
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), dotenv);
|
||||||
let source = serde_json::json!({"path": "${KSP_LOGS_DIRECTORY}", "items": [1, "${KSP_LOGS_DIRECTORY}"], "enabled": true});
|
let source = serde_json::json!({"path": "${KSP_LOGS_DIRECTORY}", "items": [1, "${KSP_LOGS_DIRECTORY}"], "enabled": true});
|
||||||
let resolved = environment.resolve_json(&source);
|
let resolved = environment.resolve_json(&source);
|
||||||
assert!(resolved.is_ok(), "recursive JSON resolution should succeed");
|
assert!(resolved.is_ok(), "recursive JSON resolution should succeed");
|
||||||
@@ -137,7 +137,7 @@ fn json_resolver_walks_objects_and_arrays_without_changing_keys() {
|
|||||||
fn dotenv_parser_supports_comments_export_quotes_empty_values_and_ignores_external_keys() {
|
fn dotenv_parser_supports_comments_export_quotes_empty_values_and_ignores_external_keys() {
|
||||||
let path = std::path::Path::new("fixture.env");
|
let path = std::path::Path::new("fixture.env");
|
||||||
let content = "# comment\nexport KSP_LOGS_DIRECTORY = 'quoted logs'\nOTHER_TOOL=value\n";
|
let content = "# comment\nexport KSP_LOGS_DIRECTORY = 'quoted logs'\nOTHER_TOOL=value\n";
|
||||||
let parsed = super::parse_dotenv_content(path, content);
|
let parsed = crate::parse_dotenv_content(path, content);
|
||||||
assert!(parsed.is_ok(), "dotenv fixture should parse");
|
assert!(parsed.is_ok(), "dotenv fixture should parse");
|
||||||
let parsed = match parsed {
|
let parsed = match parsed {
|
||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
@@ -149,7 +149,7 @@ fn dotenv_parser_supports_comments_export_quotes_empty_values_and_ignores_extern
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn dotenv_duplicate_ksp_key_is_rejected() {
|
fn dotenv_duplicate_ksp_key_is_rejected() {
|
||||||
let parsed = super::parse_dotenv_content(std::path::Path::new("fixture.env"), "KSP_LOGS_DIRECTORY=one\nKSP_LOGS_DIRECTORY=two\n");
|
let parsed = crate::parse_dotenv_content(std::path::Path::new("fixture.env"), "KSP_LOGS_DIRECTORY=one\nKSP_LOGS_DIRECTORY=two\n");
|
||||||
let error = match parsed {
|
let error = match parsed {
|
||||||
std::result::Result::Ok(_) => return,
|
std::result::Result::Ok(_) => return,
|
||||||
std::result::Result::Err(error) => error,
|
std::result::Result::Err(error) => error,
|
||||||
@@ -199,7 +199,7 @@ fn logging_fixture_profile_resolves_environment_fallback_without_changing_source
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(_) => return,
|
std::result::Result::Err(_) => return,
|
||||||
};
|
};
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||||
let effective = profile.resolve_effective_environment(&environment);
|
let effective = profile.resolve_effective_environment(&environment);
|
||||||
assert!(effective.is_ok(), "committed Logging environment fallback should resolve");
|
assert!(effective.is_ok(), "committed Logging environment fallback should resolve");
|
||||||
let effective = match effective {
|
let effective = match effective {
|
||||||
@@ -245,7 +245,7 @@ fn secret_environment_value_keeps_real_value_but_redacts_safe_and_debug_views()
|
|||||||
let canary = "KSP_SECRET_CANARY_91b7c6";
|
let canary = "KSP_SECRET_CANARY_91b7c6";
|
||||||
let mut process = std::collections::BTreeMap::<String, String>::new();
|
let mut process = std::collections::BTreeMap::<String, String>::new();
|
||||||
process.insert("KSP_SECRET_TEST_TOKEN".to_owned(), canary.to_owned());
|
process.insert("KSP_SECRET_TEST_TOKEN".to_owned(), canary.to_owned());
|
||||||
let environment = super::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
|
||||||
let resolved = environment.resolve_variable("KSP_SECRET_TEST_TOKEN", std::option::Option::None);
|
let resolved = environment.resolve_variable("KSP_SECRET_TEST_TOKEN", std::option::Option::None);
|
||||||
assert!(resolved.is_ok(), "secret process value should resolve");
|
assert!(resolved.is_ok(), "secret process value should resolve");
|
||||||
let resolved = match resolved {
|
let resolved = match resolved {
|
||||||
@@ -255,7 +255,7 @@ fn secret_environment_value_keeps_real_value_but_redacts_safe_and_debug_views()
|
|||||||
assert_eq!(resolved.value(), canary);
|
assert_eq!(resolved.value(), canary);
|
||||||
assert_eq!(resolved.safe_value(), crate::REDACTED_CONFIG_VALUE);
|
assert_eq!(resolved.safe_value(), crate::REDACTED_CONFIG_VALUE);
|
||||||
assert_eq!(resolved.sensitivity(), crate::ConfigSensitivity::Secret);
|
assert_eq!(resolved.sensitivity(), crate::ConfigSensitivity::Secret);
|
||||||
assert_eq!(resolved.source(), super::ConfigEnvironmentSource::Process);
|
assert_eq!(resolved.source(), crate::ConfigEnvironmentSource::Process);
|
||||||
let debug = format!("{resolved:?}");
|
let debug = format!("{resolved:?}");
|
||||||
assert!(!debug.contains(canary), "Debug must not reveal the secret canary");
|
assert!(!debug.contains(canary), "Debug must not reveal the secret canary");
|
||||||
assert!(debug.contains(crate::REDACTED_CONFIG_VALUE));
|
assert!(debug.contains(crate::REDACTED_CONFIG_VALUE));
|
||||||
@@ -267,7 +267,7 @@ fn detailed_text_redacts_only_secret_segments_and_keeps_ordered_provenance() {
|
|||||||
let mut process = std::collections::BTreeMap::<String, String>::new();
|
let mut process = std::collections::BTreeMap::<String, String>::new();
|
||||||
process.insert("KSP_PUBLIC_HOST".to_owned(), "rpc.example.test".to_owned());
|
process.insert("KSP_PUBLIC_HOST".to_owned(), "rpc.example.test".to_owned());
|
||||||
process.insert("KSP_SECRET_TOKEN".to_owned(), secret.to_owned());
|
process.insert("KSP_SECRET_TOKEN".to_owned(), secret.to_owned());
|
||||||
let environment = super::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(process, std::collections::BTreeMap::new());
|
||||||
let resolved = environment.resolve_text_detailed("https://${KSP_PUBLIC_HOST}/?token=${KSP_SECRET_TOKEN}");
|
let resolved = environment.resolve_text_detailed("https://${KSP_PUBLIC_HOST}/?token=${KSP_SECRET_TOKEN}");
|
||||||
assert!(resolved.is_ok(), "composed secret URL should resolve");
|
assert!(resolved.is_ok(), "composed secret URL should resolve");
|
||||||
let resolved = match resolved {
|
let resolved = match resolved {
|
||||||
@@ -288,7 +288,7 @@ fn detailed_text_redacts_only_secret_segments_and_keeps_ordered_provenance() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn secret_fallback_inherits_secret_sensitivity_and_is_redacted() {
|
fn secret_fallback_inherits_secret_sensitivity_and_is_redacted() {
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||||
let resolved = environment.resolve_text_detailed("token=${KSP_SECRET_TOKEN:-false-secret}");
|
let resolved = environment.resolve_text_detailed("token=${KSP_SECRET_TOKEN:-false-secret}");
|
||||||
assert!(resolved.is_ok(), "secret fallback should resolve");
|
assert!(resolved.is_ok(), "secret fallback should resolve");
|
||||||
let resolved = match resolved {
|
let resolved = match resolved {
|
||||||
@@ -298,7 +298,7 @@ fn secret_fallback_inherits_secret_sensitivity_and_is_redacted() {
|
|||||||
assert_eq!(resolved.value(), "token=false-secret");
|
assert_eq!(resolved.value(), "token=false-secret");
|
||||||
assert_eq!(resolved.safe_value(), "token=********");
|
assert_eq!(resolved.safe_value(), "token=********");
|
||||||
assert_eq!(resolved.sensitivity(), crate::ConfigSensitivity::Secret);
|
assert_eq!(resolved.sensitivity(), crate::ConfigSensitivity::Secret);
|
||||||
assert_eq!(resolved.provenance()[1].environment_source(), std::option::Option::Some(super::ConfigEnvironmentSource::Fallback));
|
assert_eq!(resolved.provenance()[1].environment_source(), std::option::Option::Some(crate::ConfigEnvironmentSource::Fallback));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -306,7 +306,7 @@ fn detailed_json_preserves_safe_tree_sensitivity_and_pointer_provenance() {
|
|||||||
let secret = "nested-secret-canary-2d11";
|
let secret = "nested-secret-canary-2d11";
|
||||||
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
let mut dotenv = std::collections::BTreeMap::<String, String>::new();
|
||||||
dotenv.insert("KSP_SECRET_TOKEN".to_owned(), secret.to_owned());
|
dotenv.insert("KSP_SECRET_TOKEN".to_owned(), secret.to_owned());
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), dotenv);
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), dotenv);
|
||||||
let source = serde_json::json!({"transport": {"url": "https://host/?token=${KSP_SECRET_TOKEN}"}, "items": ["plain", 7]});
|
let source = serde_json::json!({"transport": {"url": "https://host/?token=${KSP_SECRET_TOKEN}"}, "items": ["plain", 7]});
|
||||||
let resolved = environment.resolve_json_detailed(&source);
|
let resolved = environment.resolve_json_detailed(&source);
|
||||||
assert!(resolved.is_ok(), "detailed JSON should resolve");
|
assert!(resolved.is_ok(), "detailed JSON should resolve");
|
||||||
@@ -349,7 +349,7 @@ fn detailed_fixture_profile_environment_keeps_global_origin_and_adds_environment
|
|||||||
std::result::Result::Ok(value) => value,
|
std::result::Result::Ok(value) => value,
|
||||||
std::result::Result::Err(_) => return,
|
std::result::Result::Err(_) => return,
|
||||||
};
|
};
|
||||||
let environment = super::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
let environment = crate::ConfigEnvironment::from_maps(std::collections::BTreeMap::new(), std::collections::BTreeMap::new());
|
||||||
let effective = profile.resolve_effective_environment_detailed(&environment);
|
let effective = profile.resolve_effective_environment_detailed(&environment);
|
||||||
assert!(effective.is_ok(), "detailed committed Logging profile should resolve");
|
assert!(effective.is_ok(), "detailed committed Logging profile should resolve");
|
||||||
let effective = match effective {
|
let effective = match effective {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-config-lib/unit_tests/profile.rs
|
// file: crates/ksp-config-lib/unit_tests/profile.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fixture_default_profile_resolves_globals_profile_and_provenance() {
|
fn fixture_default_profile_resolves_globals_profile_and_provenance() {
|
||||||
@@ -15,9 +15,9 @@ fn fixture_default_profile_resolves_globals_profile_and_provenance() {
|
|||||||
if let (std::result::Result::Ok(document), std::result::Result::Ok(resolved)) = (document, resolved) {
|
if let (std::result::Result::Ok(document), std::result::Result::Ok(resolved)) = (document, resolved) {
|
||||||
let default_profile = document.value().get("default_profile").and_then(serde_json::Value::as_str);
|
let default_profile = document.value().get("default_profile").and_then(serde_json::Value::as_str);
|
||||||
assert_eq!(default_profile, std::option::Option::Some(resolved.profile_id()));
|
assert_eq!(default_profile, std::option::Option::Some(resolved.profile_id()));
|
||||||
assert_eq!(resolved.selection_source(), super::ConfigProfileSelectionSource::DefaultProfile);
|
assert_eq!(resolved.selection_source(), crate::ConfigProfileSelectionSource::DefaultProfile);
|
||||||
assert_eq!(resolved.origin("logs_directory"), std::option::Option::Some(super::ConfigValueOrigin::Global));
|
assert_eq!(resolved.origin("logs_directory"), std::option::Option::Some(crate::ConfigValueOrigin::Global));
|
||||||
assert_eq!(resolved.origin("default_filter"), std::option::Option::Some(super::ConfigValueOrigin::Profile));
|
assert_eq!(resolved.origin("default_filter"), std::option::Option::Some(crate::ConfigValueOrigin::Profile));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
resolved.profile().get("default_filter").and_then(serde_json::Value::as_str),
|
resolved.profile().get("default_filter").and_then(serde_json::Value::as_str),
|
||||||
resolved.effective().get("default_filter").and_then(serde_json::Value::as_str),
|
resolved.effective().get("default_filter").and_then(serde_json::Value::as_str),
|
||||||
@@ -45,7 +45,7 @@ fn explicit_profile_selection_is_distinct_from_default_selection() {
|
|||||||
assert!(resolved.is_ok(), "explicit committed profile should resolve: {resolved:?}");
|
assert!(resolved.is_ok(), "explicit committed profile should resolve: {resolved:?}");
|
||||||
if let std::result::Result::Ok(resolved) = resolved {
|
if let std::result::Result::Ok(resolved) = resolved {
|
||||||
assert_eq!(resolved.profile_id(), profile_id);
|
assert_eq!(resolved.profile_id(), profile_id);
|
||||||
assert_eq!(resolved.selection_source(), super::ConfigProfileSelectionSource::Explicit);
|
assert_eq!(resolved.selection_source(), crate::ConfigProfileSelectionSource::Explicit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,49 +1,49 @@
|
|||||||
// file: crates/ksp-config-lib/unit_tests/registry.rs
|
// file: crates/ksp-config-lib/unit_tests/registry.rs
|
||||||
// version: 5
|
// version: 6
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn descriptors_expose_complete_registry_in_deterministic_file_id_order() {
|
fn descriptors_expose_complete_registry_in_deterministic_file_id_order() {
|
||||||
let registry = super::ConfigFileRegistry::defaults();
|
let registry = crate::ConfigFileRegistry::defaults();
|
||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
if let std::result::Result::Ok(registry) = registry {
|
if let std::result::Result::Ok(registry) = registry {
|
||||||
let descriptors: std::vec::Vec<&super::ConfigFileDescriptor> = registry.descriptors().collect();
|
let descriptors: std::vec::Vec<&crate::ConfigFileDescriptor> = registry.descriptors().collect();
|
||||||
assert_eq!(descriptors.len(), 5);
|
assert_eq!(descriptors.len(), 5);
|
||||||
assert_eq!(descriptors[0].file_id().as_str(), super::FILE_ID_STD_LOGGING);
|
assert_eq!(descriptors[0].file_id().as_str(), crate::FILE_ID_STD_LOGGING);
|
||||||
assert_eq!(descriptors[0].kind(), super::ConfigFileKind::Config);
|
assert_eq!(descriptors[0].kind(), crate::ConfigFileKind::Config);
|
||||||
assert_eq!(descriptors[0].filename(), std::path::Path::new(super::DEFAULT_STD_LOGGING_FILENAME));
|
assert_eq!(descriptors[0].filename(), std::path::Path::new(crate::DEFAULT_STD_LOGGING_FILENAME));
|
||||||
let logging_schema_file_id = descriptors[0].schema_file_id();
|
let logging_schema_file_id = descriptors[0].schema_file_id();
|
||||||
assert!(logging_schema_file_id.is_some(), "logging descriptor should expose its validation schema");
|
assert!(logging_schema_file_id.is_some(), "logging descriptor should expose its validation schema");
|
||||||
if let std::option::Option::Some(schema_file_id) = logging_schema_file_id {
|
if let std::option::Option::Some(schema_file_id) = logging_schema_file_id {
|
||||||
assert_eq!(schema_file_id.as_str(), super::FILE_ID_SCHEMA_STD_LOGGING);
|
assert_eq!(schema_file_id.as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||||
}
|
}
|
||||||
assert_eq!(descriptors[1].file_id().as_str(), super::FILE_ID_STD_TRANSPORT);
|
assert_eq!(descriptors[1].file_id().as_str(), crate::FILE_ID_STD_TRANSPORT);
|
||||||
assert_eq!(descriptors[1].kind(), super::ConfigFileKind::Config);
|
assert_eq!(descriptors[1].kind(), crate::ConfigFileKind::Config);
|
||||||
assert_eq!(descriptors[1].filename(), std::path::Path::new(super::DEFAULT_STD_TRANSPORT_FILENAME));
|
assert_eq!(descriptors[1].filename(), std::path::Path::new(crate::DEFAULT_STD_TRANSPORT_FILENAME));
|
||||||
let transport_schema_file_id = descriptors[1].schema_file_id();
|
let transport_schema_file_id = descriptors[1].schema_file_id();
|
||||||
assert!(transport_schema_file_id.is_some(), "transport descriptor should expose its validation schema");
|
assert!(transport_schema_file_id.is_some(), "transport descriptor should expose its validation schema");
|
||||||
if let std::option::Option::Some(schema_file_id) = transport_schema_file_id {
|
if let std::option::Option::Some(schema_file_id) = transport_schema_file_id {
|
||||||
assert_eq!(schema_file_id.as_str(), super::FILE_ID_SCHEMA_STD_TRANSPORT);
|
assert_eq!(schema_file_id.as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT);
|
||||||
}
|
}
|
||||||
assert_eq!(descriptors[2].file_id().as_str(), super::FILE_ID_SCHEMA_COMPOSITE);
|
assert_eq!(descriptors[2].file_id().as_str(), crate::FILE_ID_SCHEMA_COMPOSITE);
|
||||||
assert_eq!(descriptors[2].kind(), super::ConfigFileKind::Schema);
|
assert_eq!(descriptors[2].kind(), crate::ConfigFileKind::Schema);
|
||||||
assert_eq!(descriptors[3].file_id().as_str(), super::FILE_ID_SCHEMA_STD_LOGGING);
|
assert_eq!(descriptors[3].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||||
assert_eq!(descriptors[3].kind(), super::ConfigFileKind::Schema);
|
assert_eq!(descriptors[3].kind(), crate::ConfigFileKind::Schema);
|
||||||
assert_eq!(descriptors[4].file_id().as_str(), super::FILE_ID_SCHEMA_STD_TRANSPORT);
|
assert_eq!(descriptors[4].file_id().as_str(), crate::FILE_ID_SCHEMA_STD_TRANSPORT);
|
||||||
assert_eq!(descriptors[4].kind(), super::ConfigFileKind::Schema);
|
assert_eq!(descriptors[4].kind(), crate::ConfigFileKind::Schema);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn descriptors_reflect_filename_overrides_without_changing_logical_metadata() {
|
fn descriptors_reflect_filename_overrides_without_changing_logical_metadata() {
|
||||||
let registry = super::ConfigFileRegistry::defaults();
|
let registry = crate::ConfigFileRegistry::defaults();
|
||||||
let file_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
assert!(file_id.is_ok(), "logging file_id should be valid: {file_id:?}");
|
assert!(file_id.is_ok(), "logging file_id should be valid: {file_id:?}");
|
||||||
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
||||||
let overridden = registry.with_filename_override(&file_id, "profiles/desktop.logging.json");
|
let overridden = registry.with_filename_override(&file_id, "profiles/desktop.logging.json");
|
||||||
assert!(overridden.is_ok(), "filename override should remain valid: {overridden:?}");
|
assert!(overridden.is_ok(), "filename override should remain valid: {overridden:?}");
|
||||||
if let std::result::Result::Ok(overridden) = overridden {
|
if let std::result::Result::Ok(overridden) = overridden {
|
||||||
let mut found: std::option::Option<&super::ConfigFileDescriptor> = std::option::Option::None;
|
let mut found: std::option::Option<&crate::ConfigFileDescriptor> = std::option::Option::None;
|
||||||
for descriptor in overridden.descriptors() {
|
for descriptor in overridden.descriptors() {
|
||||||
if descriptor.file_id() == &file_id {
|
if descriptor.file_id() == &file_id {
|
||||||
found = std::option::Option::Some(descriptor);
|
found = std::option::Option::Some(descriptor);
|
||||||
@@ -52,12 +52,12 @@ fn descriptors_reflect_filename_overrides_without_changing_logical_metadata() {
|
|||||||
assert!(found.is_some(), "public descriptor inventory should retain the overridden logging descriptor");
|
assert!(found.is_some(), "public descriptor inventory should retain the overridden logging descriptor");
|
||||||
if let std::option::Option::Some(descriptor) = found {
|
if let std::option::Option::Some(descriptor) = found {
|
||||||
assert_eq!(descriptor.file_id(), &file_id);
|
assert_eq!(descriptor.file_id(), &file_id);
|
||||||
assert_eq!(descriptor.kind(), super::ConfigFileKind::Config);
|
assert_eq!(descriptor.kind(), crate::ConfigFileKind::Config);
|
||||||
assert_eq!(descriptor.filename(), std::path::Path::new("profiles/desktop.logging.json"));
|
assert_eq!(descriptor.filename(), std::path::Path::new("profiles/desktop.logging.json"));
|
||||||
let schema_file_id = descriptor.schema_file_id();
|
let schema_file_id = descriptor.schema_file_id();
|
||||||
assert!(schema_file_id.is_some(), "filename override should preserve schema association");
|
assert!(schema_file_id.is_some(), "filename override should preserve schema association");
|
||||||
if let std::option::Option::Some(schema_file_id) = schema_file_id {
|
if let std::option::Option::Some(schema_file_id) = schema_file_id {
|
||||||
assert_eq!(schema_file_id.as_str(), super::FILE_ID_SCHEMA_STD_LOGGING);
|
assert_eq!(schema_file_id.as_str(), crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,11 +66,11 @@ fn descriptors_reflect_filename_overrides_without_changing_logical_metadata() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn defaults_register_logging_document_and_schema_with_distinct_roots() {
|
fn defaults_register_logging_document_and_schema_with_distinct_roots() {
|
||||||
let registry = super::ConfigFileRegistry::defaults();
|
let registry = crate::ConfigFileRegistry::defaults();
|
||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
if let std::result::Result::Ok(registry) = registry {
|
if let std::result::Result::Ok(registry) = registry {
|
||||||
let logging_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
let logging_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
||||||
let schema_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_STD_LOGGING);
|
let schema_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||||
assert!(logging_id.is_ok(), "logging file_id should be valid: {logging_id:?}");
|
assert!(logging_id.is_ok(), "logging file_id should be valid: {logging_id:?}");
|
||||||
assert!(schema_id.is_ok(), "logging schema file_id should be valid: {schema_id:?}");
|
assert!(schema_id.is_ok(), "logging schema file_id should be valid: {schema_id:?}");
|
||||||
if let (std::result::Result::Ok(logging_id), std::result::Result::Ok(schema_id)) = (logging_id, schema_id) {
|
if let (std::result::Result::Ok(logging_id), std::result::Result::Ok(schema_id)) = (logging_id, schema_id) {
|
||||||
@@ -79,15 +79,15 @@ fn defaults_register_logging_document_and_schema_with_distinct_roots() {
|
|||||||
assert!(logging.is_ok(), "logging descriptor should exist: {logging:?}");
|
assert!(logging.is_ok(), "logging descriptor should exist: {logging:?}");
|
||||||
assert!(schema.is_ok(), "logging schema descriptor should exist: {schema:?}");
|
assert!(schema.is_ok(), "logging schema descriptor should exist: {schema:?}");
|
||||||
if let (std::result::Result::Ok(logging), std::result::Result::Ok(schema)) = (logging, schema) {
|
if let (std::result::Result::Ok(logging), std::result::Result::Ok(schema)) = (logging, schema) {
|
||||||
assert_eq!(logging.kind(), super::ConfigFileKind::Config);
|
assert_eq!(logging.kind(), crate::ConfigFileKind::Config);
|
||||||
assert_eq!(logging.filename(), std::path::Path::new(super::DEFAULT_STD_LOGGING_FILENAME));
|
assert_eq!(logging.filename(), std::path::Path::new(crate::DEFAULT_STD_LOGGING_FILENAME));
|
||||||
let logging_schema = logging.schema_file_id();
|
let logging_schema = logging.schema_file_id();
|
||||||
assert!(logging_schema.is_some(), "logging document should declare its validation schema");
|
assert!(logging_schema.is_some(), "logging document should declare its validation schema");
|
||||||
if let std::option::Option::Some(logging_schema) = logging_schema {
|
if let std::option::Option::Some(logging_schema) = logging_schema {
|
||||||
assert_eq!(logging_schema, &schema_id);
|
assert_eq!(logging_schema, &schema_id);
|
||||||
}
|
}
|
||||||
assert_eq!(schema.kind(), super::ConfigFileKind::Schema);
|
assert_eq!(schema.kind(), crate::ConfigFileKind::Schema);
|
||||||
assert_eq!(schema.filename(), std::path::Path::new(super::DEFAULT_STD_LOGGING_SCHEMA_FILENAME));
|
assert_eq!(schema.filename(), std::path::Path::new(crate::DEFAULT_STD_LOGGING_SCHEMA_FILENAME));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -95,11 +95,11 @@ fn defaults_register_logging_document_and_schema_with_distinct_roots() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn defaults_register_transport_document_and_schema_with_distinct_roots() {
|
fn defaults_register_transport_document_and_schema_with_distinct_roots() {
|
||||||
let registry = super::ConfigFileRegistry::defaults();
|
let registry = crate::ConfigFileRegistry::defaults();
|
||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
if let std::result::Result::Ok(registry) = registry {
|
if let std::result::Result::Ok(registry) = registry {
|
||||||
let transport_id = super::ConfigFileId::new(super::FILE_ID_STD_TRANSPORT);
|
let transport_id = crate::ConfigFileId::new(crate::FILE_ID_STD_TRANSPORT);
|
||||||
let schema_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_STD_TRANSPORT);
|
let schema_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_STD_TRANSPORT);
|
||||||
assert!(transport_id.is_ok(), "transport file_id should be valid: {transport_id:?}");
|
assert!(transport_id.is_ok(), "transport file_id should be valid: {transport_id:?}");
|
||||||
assert!(schema_id.is_ok(), "transport schema file_id should be valid: {schema_id:?}");
|
assert!(schema_id.is_ok(), "transport schema file_id should be valid: {schema_id:?}");
|
||||||
if let (std::result::Result::Ok(transport_id), std::result::Result::Ok(schema_id)) = (transport_id, schema_id) {
|
if let (std::result::Result::Ok(transport_id), std::result::Result::Ok(schema_id)) = (transport_id, schema_id) {
|
||||||
@@ -108,11 +108,11 @@ fn defaults_register_transport_document_and_schema_with_distinct_roots() {
|
|||||||
assert!(transport.is_ok(), "transport descriptor should exist: {transport:?}");
|
assert!(transport.is_ok(), "transport descriptor should exist: {transport:?}");
|
||||||
assert!(schema.is_ok(), "transport schema descriptor should exist: {schema:?}");
|
assert!(schema.is_ok(), "transport schema descriptor should exist: {schema:?}");
|
||||||
if let (std::result::Result::Ok(transport), std::result::Result::Ok(schema)) = (transport, schema) {
|
if let (std::result::Result::Ok(transport), std::result::Result::Ok(schema)) = (transport, schema) {
|
||||||
assert_eq!(transport.kind(), super::ConfigFileKind::Config);
|
assert_eq!(transport.kind(), crate::ConfigFileKind::Config);
|
||||||
assert_eq!(transport.filename(), std::path::Path::new(super::DEFAULT_STD_TRANSPORT_FILENAME));
|
assert_eq!(transport.filename(), std::path::Path::new(crate::DEFAULT_STD_TRANSPORT_FILENAME));
|
||||||
assert_eq!(transport.schema_file_id(), std::option::Option::Some(&schema_id));
|
assert_eq!(transport.schema_file_id(), std::option::Option::Some(&schema_id));
|
||||||
assert_eq!(schema.kind(), super::ConfigFileKind::Schema);
|
assert_eq!(schema.kind(), crate::ConfigFileKind::Schema);
|
||||||
assert_eq!(schema.filename(), std::path::Path::new(super::DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME));
|
assert_eq!(schema.filename(), std::path::Path::new(crate::DEFAULT_STD_TRANSPORT_SCHEMA_FILENAME));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,13 +120,13 @@ fn defaults_register_transport_document_and_schema_with_distinct_roots() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn resolve_path_uses_descriptor_kind_to_select_bootstrap_root() {
|
fn resolve_path_uses_descriptor_kind_to_select_bootstrap_root() {
|
||||||
let registry = super::ConfigFileRegistry::defaults();
|
let registry = crate::ConfigFileRegistry::defaults();
|
||||||
let bootstrap = crate::ConfigBootstrapOptions::from_paths("runtime-config", "runtime-schemas");
|
let bootstrap = crate::ConfigBootstrapOptions::from_paths("runtime-config", "runtime-schemas");
|
||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
assert!(bootstrap.is_ok(), "bootstrap paths should be valid: {bootstrap:?}");
|
assert!(bootstrap.is_ok(), "bootstrap paths should be valid: {bootstrap:?}");
|
||||||
if let (std::result::Result::Ok(registry), std::result::Result::Ok(bootstrap)) = (registry, bootstrap) {
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(bootstrap)) = (registry, bootstrap) {
|
||||||
let logging_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
let logging_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
||||||
let schema_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_STD_LOGGING);
|
let schema_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||||
if let (std::result::Result::Ok(logging_id), std::result::Result::Ok(schema_id)) = (logging_id, schema_id) {
|
if let (std::result::Result::Ok(logging_id), std::result::Result::Ok(schema_id)) = (logging_id, schema_id) {
|
||||||
let logging = registry.resolve_path(&bootstrap, &logging_id);
|
let logging = registry.resolve_path(&bootstrap, &logging_id);
|
||||||
let schema = registry.resolve_path(&bootstrap, &schema_id);
|
let schema = registry.resolve_path(&bootstrap, &schema_id);
|
||||||
@@ -150,16 +150,16 @@ fn cli_filemap_override_replaces_filename_and_last_value_wins() {
|
|||||||
std::ffi::OsString::from("--other-option"),
|
std::ffi::OsString::from("--other-option"),
|
||||||
std::ffi::OsString::from("--filemap=cfg.std.logging=profiles/custom.logging.json"),
|
std::ffi::OsString::from("--filemap=cfg.std.logging=profiles/custom.logging.json"),
|
||||||
];
|
];
|
||||||
let registry = super::ConfigFileRegistry::from_args(&args);
|
let registry = crate::ConfigFileRegistry::from_args(&args);
|
||||||
assert!(registry.is_ok(), "filemap overrides should parse: {registry:?}");
|
assert!(registry.is_ok(), "filemap overrides should parse: {registry:?}");
|
||||||
if let std::result::Result::Ok(registry) = registry {
|
if let std::result::Result::Ok(registry) = registry {
|
||||||
let file_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
||||||
if let std::result::Result::Ok(file_id) = file_id {
|
if let std::result::Result::Ok(file_id) = file_id {
|
||||||
let descriptor = registry.descriptor(&file_id);
|
let descriptor = registry.descriptor(&file_id);
|
||||||
assert!(descriptor.is_ok(), "logging descriptor should remain registered: {descriptor:?}");
|
assert!(descriptor.is_ok(), "logging descriptor should remain registered: {descriptor:?}");
|
||||||
if let std::result::Result::Ok(descriptor) = descriptor {
|
if let std::result::Result::Ok(descriptor) = descriptor {
|
||||||
assert_eq!(descriptor.filename(), std::path::Path::new("profiles/custom.logging.json"));
|
assert_eq!(descriptor.filename(), std::path::Path::new("profiles/custom.logging.json"));
|
||||||
assert_eq!(descriptor.kind(), super::ConfigFileKind::Config);
|
assert_eq!(descriptor.kind(), crate::ConfigFileKind::Config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,8 +167,8 @@ fn cli_filemap_override_replaces_filename_and_last_value_wins() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn programmatic_override_preserves_file_id_and_kind() {
|
fn programmatic_override_preserves_file_id_and_kind() {
|
||||||
let registry = super::ConfigFileRegistry::defaults();
|
let registry = crate::ConfigFileRegistry::defaults();
|
||||||
let file_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_STD_LOGGING);
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_STD_LOGGING);
|
||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
assert!(file_id.is_ok(), "schema file_id should be valid: {file_id:?}");
|
assert!(file_id.is_ok(), "schema file_id should be valid: {file_id:?}");
|
||||||
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
||||||
@@ -178,7 +178,7 @@ fn programmatic_override_preserves_file_id_and_kind() {
|
|||||||
let descriptor = overridden.descriptor(&file_id);
|
let descriptor = overridden.descriptor(&file_id);
|
||||||
if let std::result::Result::Ok(descriptor) = descriptor {
|
if let std::result::Result::Ok(descriptor) = descriptor {
|
||||||
assert_eq!(descriptor.file_id(), &file_id);
|
assert_eq!(descriptor.file_id(), &file_id);
|
||||||
assert_eq!(descriptor.kind(), super::ConfigFileKind::Schema);
|
assert_eq!(descriptor.kind(), crate::ConfigFileKind::Schema);
|
||||||
assert_eq!(descriptor.filename(), std::path::Path::new("alternate/logging.schema.json"));
|
assert_eq!(descriptor.filename(), std::path::Path::new("alternate/logging.schema.json"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -188,7 +188,7 @@ fn programmatic_override_preserves_file_id_and_kind() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn unknown_file_id_override_is_rejected() {
|
fn unknown_file_id_override_is_rejected() {
|
||||||
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--filemap=cfg.unknown=unknown.json")];
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from("--filemap=cfg.unknown=unknown.json")];
|
||||||
let result = super::ConfigFileRegistry::from_args(&args);
|
let result = crate::ConfigFileRegistry::from_args(&args);
|
||||||
assert!(result.is_err(), "unknown logical files must not be introduced by CLI override");
|
assert!(result.is_err(), "unknown logical files must not be introduced by CLI override");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_UNKNOWN);
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_UNKNOWN);
|
||||||
@@ -198,7 +198,7 @@ fn unknown_file_id_override_is_rejected() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn invalid_file_ids_are_rejected() {
|
fn invalid_file_ids_are_rejected() {
|
||||||
for value in ["", ".cfg", "cfg.", "cfg..logging", "CFG.logging", "cfg/logging"] {
|
for value in ["", ".cfg", "cfg.", "cfg..logging", "CFG.logging", "cfg/logging"] {
|
||||||
let result = super::ConfigFileId::new(value);
|
let result = crate::ConfigFileId::new(value);
|
||||||
assert!(result.is_err(), "invalid file_id must be rejected: {value}");
|
assert!(result.is_err(), "invalid file_id must be rejected: {value}");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_INVALID);
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_INVALID);
|
||||||
@@ -208,8 +208,8 @@ fn invalid_file_ids_are_rejected() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn absolute_and_traversing_filenames_are_rejected() {
|
fn absolute_and_traversing_filenames_are_rejected() {
|
||||||
let registry = super::ConfigFileRegistry::defaults();
|
let registry = crate::ConfigFileRegistry::defaults();
|
||||||
let file_id = super::ConfigFileId::new(super::FILE_ID_STD_LOGGING);
|
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_LOGGING);
|
||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
assert!(file_id.is_ok(), "logging file_id should be valid: {file_id:?}");
|
assert!(file_id.is_ok(), "logging file_id should be valid: {file_id:?}");
|
||||||
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
if let (std::result::Result::Ok(registry), std::result::Result::Ok(file_id)) = (registry, file_id) {
|
||||||
@@ -226,7 +226,7 @@ fn absolute_and_traversing_filenames_are_rejected() {
|
|||||||
fn malformed_filemap_arguments_are_rejected() {
|
fn malformed_filemap_arguments_are_rejected() {
|
||||||
for argument in ["--filemap", "--filemap=cfg.std.logging", "--filemap==logging.json", "--filemap=cfg.std.logging="] {
|
for argument in ["--filemap", "--filemap=cfg.std.logging", "--filemap==logging.json", "--filemap=cfg.std.logging="] {
|
||||||
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from(argument)];
|
let args = [std::ffi::OsString::from("ksp-app"), std::ffi::OsString::from(argument)];
|
||||||
let result = super::ConfigFileRegistry::from_args(&args);
|
let result = crate::ConfigFileRegistry::from_args(&args);
|
||||||
assert!(result.is_err(), "malformed filemap argument must be rejected: {argument}");
|
assert!(result.is_err(), "malformed filemap argument must be rejected: {argument}");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
||||||
@@ -236,12 +236,12 @@ fn malformed_filemap_arguments_are_rejected() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn duplicate_registry_ids_are_rejected() {
|
fn duplicate_registry_ids_are_rejected() {
|
||||||
let first = super::ConfigFileDescriptor::new("cfg.duplicate", super::ConfigFileKind::Config, "first.json", std::option::Option::None);
|
let first = crate::ConfigFileDescriptor::new("cfg.duplicate", crate::ConfigFileKind::Config, "first.json", std::option::Option::None);
|
||||||
let second = super::ConfigFileDescriptor::new("cfg.duplicate", super::ConfigFileKind::Config, "second.json", std::option::Option::None);
|
let second = crate::ConfigFileDescriptor::new("cfg.duplicate", crate::ConfigFileKind::Config, "second.json", std::option::Option::None);
|
||||||
assert!(first.is_ok(), "first descriptor should be valid: {first:?}");
|
assert!(first.is_ok(), "first descriptor should be valid: {first:?}");
|
||||||
assert!(second.is_ok(), "second descriptor should be valid: {second:?}");
|
assert!(second.is_ok(), "second descriptor should be valid: {second:?}");
|
||||||
if let (std::result::Result::Ok(first), std::result::Result::Ok(second)) = (first, second) {
|
if let (std::result::Result::Ok(first), std::result::Result::Ok(second)) = (first, second) {
|
||||||
let result = super::build_registry([first, second]);
|
let result = crate::build_registry([first, second]);
|
||||||
assert!(result.is_err(), "duplicate file_ids must be rejected");
|
assert!(result.is_err(), "duplicate file_ids must be rejected");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_DUPLICATE);
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_DUPLICATE);
|
||||||
@@ -251,7 +251,7 @@ fn duplicate_registry_ids_are_rejected() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn descriptor_kind_must_match_file_id_namespace() {
|
fn descriptor_kind_must_match_file_id_namespace() {
|
||||||
let result = super::ConfigFileDescriptor::new("schema.invalid-kind", super::ConfigFileKind::Config, "invalid.json", std::option::Option::None);
|
let result = crate::ConfigFileDescriptor::new("schema.invalid-kind", crate::ConfigFileKind::Config, "invalid.json", std::option::Option::None);
|
||||||
assert!(result.is_err(), "descriptor kind mismatch must be rejected");
|
assert!(result.is_err(), "descriptor kind mismatch must be rejected");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
||||||
@@ -260,10 +260,10 @@ fn descriptor_kind_must_match_file_id_namespace() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn config_schema_association_must_reference_registered_schema_descriptor() {
|
fn config_schema_association_must_reference_registered_schema_descriptor() {
|
||||||
let config = super::ConfigFileDescriptor::new("cfg.test", super::ConfigFileKind::Config, "test.json", std::option::Option::Some("schema.test"));
|
let config = crate::ConfigFileDescriptor::new("cfg.test", crate::ConfigFileKind::Config, "test.json", std::option::Option::Some("schema.test"));
|
||||||
assert!(config.is_ok(), "config descriptor should be valid before registry association validation: {config:?}");
|
assert!(config.is_ok(), "config descriptor should be valid before registry association validation: {config:?}");
|
||||||
if let std::result::Result::Ok(config) = config {
|
if let std::result::Result::Ok(config) = config {
|
||||||
let result = super::build_registry([config]);
|
let result = crate::build_registry([config]);
|
||||||
assert!(result.is_err(), "registry must reject a missing schema association");
|
assert!(result.is_err(), "registry must reject a missing schema association");
|
||||||
if let std::result::Result::Err(error) = result {
|
if let std::result::Result::Err(error) = result {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_MAPPING_INVALID);
|
||||||
@@ -286,9 +286,9 @@ fn absolute_fixture_path() -> std::path::PathBuf {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn defaults_register_generic_composite_schema_without_runtime_composite() {
|
fn defaults_register_generic_composite_schema_without_runtime_composite() {
|
||||||
let registry = super::ConfigFileRegistry::defaults();
|
let registry = crate::ConfigFileRegistry::defaults();
|
||||||
let schema_id = super::ConfigFileId::new(super::FILE_ID_SCHEMA_COMPOSITE);
|
let schema_id = crate::ConfigFileId::new(crate::FILE_ID_SCHEMA_COMPOSITE);
|
||||||
let runtime_id = super::ConfigFileId::new("cfg.composite.ksp-app-wallet-desk");
|
let runtime_id = crate::ConfigFileId::new("cfg.composite.ksp-app-wallet-desk");
|
||||||
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
assert!(registry.is_ok(), "default registry should be valid: {registry:?}");
|
||||||
assert!(schema_id.is_ok(), "composite schema file_id should be valid: {schema_id:?}");
|
assert!(schema_id.is_ok(), "composite schema file_id should be valid: {schema_id:?}");
|
||||||
assert!(runtime_id.is_ok(), "future composite runtime file_id syntax should be valid: {runtime_id:?}");
|
assert!(runtime_id.is_ok(), "future composite runtime file_id syntax should be valid: {runtime_id:?}");
|
||||||
@@ -298,8 +298,8 @@ fn defaults_register_generic_composite_schema_without_runtime_composite() {
|
|||||||
assert!(schema.is_ok(), "generic composite schema should be registered: {schema:?}");
|
assert!(schema.is_ok(), "generic composite schema should be registered: {schema:?}");
|
||||||
assert!(runtime.is_err(), "no fictitious runtime composite should be registered");
|
assert!(runtime.is_err(), "no fictitious runtime composite should be registered");
|
||||||
if let std::result::Result::Ok(schema) = schema {
|
if let std::result::Result::Ok(schema) = schema {
|
||||||
assert_eq!(schema.kind(), super::ConfigFileKind::Schema);
|
assert_eq!(schema.kind(), crate::ConfigFileKind::Schema);
|
||||||
assert_eq!(schema.filename(), std::path::Path::new(super::DEFAULT_COMPOSITE_SCHEMA_FILENAME));
|
assert_eq!(schema.filename(), std::path::Path::new(crate::DEFAULT_COMPOSITE_SCHEMA_FILENAME));
|
||||||
}
|
}
|
||||||
if let std::result::Result::Err(error) = runtime {
|
if let std::result::Result::Err(error) = runtime {
|
||||||
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_UNKNOWN);
|
assert_eq!(error.code(), crate::ERROR_CODE_FILE_ID_UNKNOWN);
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
// file: crates/ksp-config-lib/unit_tests/sensitivity.rs
|
// file: crates/ksp-config-lib/unit_tests/sensitivity.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn environment_names_map_to_expected_sensitivity() {
|
fn environment_names_map_to_expected_sensitivity() {
|
||||||
assert_eq!(super::ConfigSensitivity::from_variable_name("KSP_PUBLIC_ENDPOINT").ok(), std::option::Option::Some(super::ConfigSensitivity::Public));
|
assert_eq!(crate::ConfigSensitivity::from_variable_name("KSP_PUBLIC_ENDPOINT").ok(), std::option::Option::Some(crate::ConfigSensitivity::Public));
|
||||||
assert_eq!(super::ConfigSensitivity::from_variable_name("KSP_MODE").ok(), std::option::Option::Some(super::ConfigSensitivity::Internal));
|
assert_eq!(crate::ConfigSensitivity::from_variable_name("KSP_MODE").ok(), std::option::Option::Some(crate::ConfigSensitivity::Internal));
|
||||||
assert_eq!(super::ConfigSensitivity::from_variable_name("KSP_SECRET_PASSWORD").ok(), std::option::Option::Some(super::ConfigSensitivity::Secret));
|
assert_eq!(crate::ConfigSensitivity::from_variable_name("KSP_SECRET_PASSWORD").ok(), std::option::Option::Some(crate::ConfigSensitivity::Secret));
|
||||||
assert_eq!(super::ConfigSensitivity::from_variable_name("KSPB_PUBLIC_ENDPOINT").ok(), std::option::Option::Some(super::ConfigSensitivity::Public));
|
assert_eq!(crate::ConfigSensitivity::from_variable_name("KSPB_PUBLIC_ENDPOINT").ok(), std::option::Option::Some(crate::ConfigSensitivity::Public));
|
||||||
assert_eq!(super::ConfigSensitivity::from_variable_name("KSPB_MODE").ok(), std::option::Option::Some(super::ConfigSensitivity::Internal));
|
assert_eq!(crate::ConfigSensitivity::from_variable_name("KSPB_MODE").ok(), std::option::Option::Some(crate::ConfigSensitivity::Internal));
|
||||||
assert_eq!(super::ConfigSensitivity::from_variable_name("KSPB_SECRET_PASSWORD").ok(), std::option::Option::Some(super::ConfigSensitivity::Secret));
|
assert_eq!(crate::ConfigSensitivity::from_variable_name("KSPB_SECRET_PASSWORD").ok(), std::option::Option::Some(crate::ConfigSensitivity::Secret));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn strongest_sensitivity_follows_secret_internal_public_order() {
|
fn strongest_sensitivity_follows_secret_internal_public_order() {
|
||||||
assert_eq!(super::ConfigSensitivity::Public.strongest(super::ConfigSensitivity::Internal), super::ConfigSensitivity::Internal);
|
assert_eq!(crate::ConfigSensitivity::Public.strongest(crate::ConfigSensitivity::Internal), crate::ConfigSensitivity::Internal);
|
||||||
assert_eq!(super::ConfigSensitivity::Internal.strongest(super::ConfigSensitivity::Secret), super::ConfigSensitivity::Secret);
|
assert_eq!(crate::ConfigSensitivity::Internal.strongest(crate::ConfigSensitivity::Secret), crate::ConfigSensitivity::Secret);
|
||||||
assert_eq!(super::ConfigSensitivity::Secret.strongest(super::ConfigSensitivity::Public), super::ConfigSensitivity::Secret);
|
assert_eq!(crate::ConfigSensitivity::Secret.strongest(crate::ConfigSensitivity::Public), crate::ConfigSensitivity::Secret);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn provenance_exposes_names_and_sources_without_values() {
|
fn provenance_exposes_names_and_sources_without_values() {
|
||||||
let process = super::ConfigValueProvenance::EnvironmentProcess { variable_name: "KSP_SECRET_TOKEN".to_owned() };
|
let process = crate::ConfigValueProvenance::EnvironmentProcess { variable_name: "KSP_SECRET_TOKEN".to_owned() };
|
||||||
let dotenv = super::ConfigValueProvenance::EnvironmentDotEnv { variable_name: "KSP_MODE".to_owned() };
|
let dotenv = crate::ConfigValueProvenance::EnvironmentDotEnv { variable_name: "KSP_MODE".to_owned() };
|
||||||
let fallback = super::ConfigValueProvenance::EnvironmentFallback { variable_name: "KSP_PUBLIC_HOST".to_owned() };
|
let fallback = crate::ConfigValueProvenance::EnvironmentFallback { variable_name: "KSP_PUBLIC_HOST".to_owned() };
|
||||||
assert_eq!(process.variable_name(), std::option::Option::Some("KSP_SECRET_TOKEN"));
|
assert_eq!(process.variable_name(), std::option::Option::Some("KSP_SECRET_TOKEN"));
|
||||||
assert_eq!(process.environment_source(), std::option::Option::Some(crate::ConfigEnvironmentSource::Process));
|
assert_eq!(process.environment_source(), std::option::Option::Some(crate::ConfigEnvironmentSource::Process));
|
||||||
assert_eq!(dotenv.environment_source(), std::option::Option::Some(crate::ConfigEnvironmentSource::DotEnv));
|
assert_eq!(dotenv.environment_source(), std::option::Option::Some(crate::ConfigEnvironmentSource::DotEnv));
|
||||||
assert_eq!(fallback.environment_source(), std::option::Option::Some(crate::ConfigEnvironmentSource::Fallback));
|
assert_eq!(fallback.environment_source(), std::option::Option::Some(crate::ConfigEnvironmentSource::Fallback));
|
||||||
assert_eq!(super::ConfigValueProvenance::DocumentLiteral.variable_name(), std::option::Option::None);
|
assert_eq!(crate::ConfigValueProvenance::DocumentLiteral.variable_name(), std::option::Option::None);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// file: crates/ksp-core-lib/src/lib.rs
|
// file: crates/ksp-core-lib/src/lib.rs
|
||||||
// version: 6
|
// version: 8
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-core-lib/src/program_ids.rs
|
// file: crates/ksp-core-lib/src/program_ids.rs
|
||||||
// version: 2
|
// version: 4
|
||||||
|
|
||||||
const DOMAIN_SOLANA: &str = "solana";
|
const DOMAIN_SOLANA: &str = "solana";
|
||||||
const FAMILY_CONSENSUS: &str = "consensus";
|
const FAMILY_CONSENSUS: &str = "consensus";
|
||||||
@@ -266,6 +266,12 @@ impl<'a> ProgramIdFilter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the canonical KSP Program ID registry.
|
||||||
|
#[must_use]
|
||||||
|
pub const fn entries() -> &'static [crate::ProgramIdEntry] {
|
||||||
|
return PROGRAM_ID_ENTRIES;
|
||||||
|
}
|
||||||
|
|
||||||
const PROGRAM_ID_ENTRIES: &[crate::ProgramIdEntry] = &[
|
const PROGRAM_ID_ENTRIES: &[crate::ProgramIdEntry] = &[
|
||||||
crate::ProgramIdEntry::new(
|
crate::ProgramIdEntry::new(
|
||||||
"solana.address_lookup_table",
|
"solana.address_lookup_table",
|
||||||
@@ -438,12 +444,6 @@ const PROGRAM_ID_ENTRIES: &[crate::ProgramIdEntry] = &[
|
|||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Returns the canonical KSP Program ID registry.
|
|
||||||
#[must_use]
|
|
||||||
pub const fn entries() -> &'static [crate::ProgramIdEntry] {
|
|
||||||
return PROGRAM_ID_ENTRIES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a lazy view of Program IDs matching all configured filter axes.
|
/// Returns a lazy view of Program IDs matching all configured filter axes.
|
||||||
pub fn program_ids<'a>(filter: crate::ProgramIdFilter<'a>) -> impl std::iter::Iterator<Item = &'static crate::ProgramIdEntry> + 'a {
|
pub fn program_ids<'a>(filter: crate::ProgramIdFilter<'a>) -> impl std::iter::Iterator<Item = &'static crate::ProgramIdEntry> + 'a {
|
||||||
return PROGRAM_ID_ENTRIES.iter().filter(move |entry| {
|
return PROGRAM_ID_ENTRIES.iter().filter(move |entry| {
|
||||||
|
|||||||
@@ -1,37 +1,15 @@
|
|||||||
// file: crates/ksp-logging-lib/src/domain.rs
|
// file: crates/ksp-logging-lib/src/domain.rs
|
||||||
// version: 1
|
// version: 3
|
||||||
|
|
||||||
std::thread_local! {
|
std::thread_local! {
|
||||||
static CURRENT_DOMAIN: std::cell::RefCell<std::option::Option<std::string::String>> = const { std::cell::RefCell::new(std::option::Option::None) };
|
static CURRENT_DOMAIN: std::cell::RefCell<std::option::Option<std::string::String>> = const { std::cell::RefCell::new(std::option::Option::None) };
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
/// Crate-internal `DomainContextLayer` state shared across the owning crate.
|
||||||
struct SpanDomain {
|
|
||||||
value: std::option::Option<std::string::String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct DomainVisitor {
|
|
||||||
value: std::option::Option<std::string::String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl tracing::field::Visit for DomainVisitor {
|
|
||||||
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
|
|
||||||
if field.name() == "domain" {
|
|
||||||
self.value = std::option::Option::Some(value.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
|
|
||||||
if field.name() == "domain" {
|
|
||||||
self.value = std::option::Option::Some(format!("{value:?}"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) struct DomainContextLayer;
|
pub(crate) struct DomainContextLayer;
|
||||||
|
|
||||||
impl DomainContextLayer {
|
impl DomainContextLayer {
|
||||||
|
/// Creates a new `DomainContextLayer` value.
|
||||||
pub(crate) const fn new() -> Self {
|
pub(crate) const fn new() -> Self {
|
||||||
return Self;
|
return Self;
|
||||||
}
|
}
|
||||||
@@ -99,6 +77,31 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||||
|
struct SpanDomain {
|
||||||
|
value: std::option::Option<std::string::String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct DomainVisitor {
|
||||||
|
value: std::option::Option<std::string::String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl tracing::field::Visit for DomainVisitor {
|
||||||
|
fn record_str(&mut self, field: &tracing::field::Field, value: &str) {
|
||||||
|
if field.name() == "domain" {
|
||||||
|
self.value = std::option::Option::Some(value.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn record_debug(&mut self, field: &tracing::field::Field, value: &dyn std::fmt::Debug) {
|
||||||
|
if field.name() == "domain" {
|
||||||
|
self.value = std::option::Option::Some(format!("{value:?}"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes the crate-internal current domain matches operation for the owning module.
|
||||||
pub(crate) fn current_domain_matches(selectors: &[std::string::String]) -> bool {
|
pub(crate) fn current_domain_matches(selectors: &[std::string::String]) -> bool {
|
||||||
if let [selector] = selectors
|
if let [selector] = selectors
|
||||||
&& selector == "*"
|
&& selector == "*"
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
// file: crates/ksp-logging-lib/src/error.rs
|
// file: crates/ksp-logging-lib/src/error.rs
|
||||||
// version: 4
|
// version: 5
|
||||||
|
|
||||||
/// Error code used when runtime logging settings are invalid.
|
|
||||||
pub const ERROR_CODE_INVALID_SETTINGS: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "invalid_settings");
|
|
||||||
/// Error code used when a global logging subscriber is already installed.
|
/// Error code used when a global logging subscriber is already installed.
|
||||||
pub const ERROR_CODE_ALREADY_INITIALIZED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "already_initialized");
|
pub const ERROR_CODE_ALREADY_INITIALIZED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "already_initialized");
|
||||||
/// Error code used when a hot reload cannot replace the active runtime layers.
|
|
||||||
pub const ERROR_CODE_RELOAD_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "reload_failed");
|
|
||||||
/// Error code used when the rolling file output cannot be initialized.
|
/// Error code used when the rolling file output cannot be initialized.
|
||||||
pub const ERROR_CODE_FILE_OUTPUT_INITIALIZATION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "file_output_initialization_failed");
|
pub const ERROR_CODE_FILE_OUTPUT_INITIALIZATION_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "file_output_initialization_failed");
|
||||||
/// Error code used when an application Logging runtime identity is invalid.
|
/// Error code used when an application Logging runtime identity is invalid.
|
||||||
pub const ERROR_CODE_INVALID_RUNTIME_IDENTITY: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "invalid_runtime_identity");
|
pub const ERROR_CODE_INVALID_RUNTIME_IDENTITY: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "invalid_runtime_identity");
|
||||||
|
/// Error code used when runtime logging settings are invalid.
|
||||||
|
pub const ERROR_CODE_INVALID_SETTINGS: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "invalid_settings");
|
||||||
|
/// Error code used when a hot reload cannot replace the active runtime layers.
|
||||||
|
pub const ERROR_CODE_RELOAD_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("logging", "reload_failed");
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-logging-lib/src/identity.rs
|
// file: crates/ksp-logging-lib/src/identity.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Stable runtime identity used to separate persistent file outputs between application launches.
|
//! Stable runtime identity used to separate persistent file outputs between application launches.
|
||||||
|
|
||||||
@@ -41,6 +41,7 @@ impl LoggingRuntimeIdentity {
|
|||||||
return self.launch_timestamp.as_str();
|
return self.launch_timestamp.as_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes the crate-internal file name prefix operation for `LoggingRuntimeIdentity`.
|
||||||
pub(crate) fn file_name_prefix(&self, configured_prefix: &str) -> std::string::String {
|
pub(crate) fn file_name_prefix(&self, configured_prefix: &str) -> std::string::String {
|
||||||
return format!("{}.{}.{}", self.application_id, self.launch_timestamp, configured_prefix);
|
return format!("{}.{}.{}", self.application_id, self.launch_timestamp, configured_prefix);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
// file: crates/ksp-logging-lib/src/lib.rs
|
// file: crates/ksp-logging-lib/src/lib.rs
|
||||||
// version: 8
|
// version: 10
|
||||||
|
|
||||||
#![warn(missing_docs)]
|
#![warn(missing_docs)]
|
||||||
#![deny(unreachable_pub)]
|
#![deny(unreachable_pub)]
|
||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
@@ -69,6 +70,17 @@ pub use self::span::Span;
|
|||||||
/// Instruments an asynchronous future with a KSP span.
|
/// Instruments an asynchronous future with a KSP span.
|
||||||
pub use self::span::instrument;
|
pub use self::span::instrument;
|
||||||
|
|
||||||
|
/// Crate-internal `DomainContextLayer` state shared across the owning crate.
|
||||||
|
pub(crate) use self::domain::DomainContextLayer;
|
||||||
|
/// Executes the crate-internal current domain matches operation for the owning module.
|
||||||
|
pub(crate) use self::domain::current_domain_matches;
|
||||||
|
/// Crate-internal `RouteMakeWriter` state shared across the owning crate.
|
||||||
|
pub(crate) use self::writer::RouteMakeWriter;
|
||||||
|
/// Crate-internal `RoutedWriter` variants used by the owning crate.
|
||||||
|
pub(crate) use self::writer::RoutedWriter;
|
||||||
|
/// Crate-internal `StripAnsiWriter` state shared across the owning crate.
|
||||||
|
pub(crate) use self::writer::StripAnsiWriter;
|
||||||
|
|
||||||
|
/// Hidden tracing crate bridge used exclusively by exported KSP logging macros.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
/// Internal macro bridge. KSP consumers must not use this reexport directly.
|
pub extern crate tracing;
|
||||||
pub extern crate tracing as __private_tracing;
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// file: crates/ksp-logging-lib/src/macros.rs
|
// file: crates/ksp-logging-lib/src/macros.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
/// Emits a KSP error event with an explicit owning target.
|
/// Emits a KSP error event with an explicit owning target.
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! error {
|
macro_rules! error {
|
||||||
(target: $target:expr, $($argument:tt)+) => {{
|
(target: $target:expr, $($argument:tt)+) => {{
|
||||||
$crate::__private_tracing::error!(target: $target, $($argument)+);
|
$crate::tracing::error!(target: $target, $($argument)+);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ macro_rules! error {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! warn {
|
macro_rules! warn {
|
||||||
(target: $target:expr, $($argument:tt)+) => {{
|
(target: $target:expr, $($argument:tt)+) => {{
|
||||||
$crate::__private_tracing::warn!(target: $target, $($argument)+);
|
$crate::tracing::warn!(target: $target, $($argument)+);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ macro_rules! warn {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! info {
|
macro_rules! info {
|
||||||
(target: $target:expr, $($argument:tt)+) => {{
|
(target: $target:expr, $($argument:tt)+) => {{
|
||||||
$crate::__private_tracing::info!(target: $target, $($argument)+);
|
$crate::tracing::info!(target: $target, $($argument)+);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +29,7 @@ macro_rules! info {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! debug {
|
macro_rules! debug {
|
||||||
(target: $target:expr, $($argument:tt)+) => {{
|
(target: $target:expr, $($argument:tt)+) => {{
|
||||||
$crate::__private_tracing::debug!(target: $target, $($argument)+);
|
$crate::tracing::debug!(target: $target, $($argument)+);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ macro_rules! debug {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! trace {
|
macro_rules! trace {
|
||||||
(target: $target:expr, $($argument:tt)+) => {{
|
(target: $target:expr, $($argument:tt)+) => {{
|
||||||
$crate::__private_tracing::trace!(target: $target, $($argument)+);
|
$crate::tracing::trace!(target: $target, $($argument)+);
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -45,10 +45,10 @@ macro_rules! trace {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! error_span {
|
macro_rules! error_span {
|
||||||
(target: $target:expr, $name:expr) => {{
|
(target: $target:expr, $name:expr) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::error_span!(target: $target, $name))
|
$crate::Span::__from_tracing($crate::tracing::error_span!(target: $target, $name))
|
||||||
}};
|
}};
|
||||||
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::error_span!(target: $target, $name, $($field)+))
|
$crate::Span::__from_tracing($crate::tracing::error_span!(target: $target, $name, $($field)+))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -56,10 +56,10 @@ macro_rules! error_span {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! warn_span {
|
macro_rules! warn_span {
|
||||||
(target: $target:expr, $name:expr) => {{
|
(target: $target:expr, $name:expr) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::warn_span!(target: $target, $name))
|
$crate::Span::__from_tracing($crate::tracing::warn_span!(target: $target, $name))
|
||||||
}};
|
}};
|
||||||
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::warn_span!(target: $target, $name, $($field)+))
|
$crate::Span::__from_tracing($crate::tracing::warn_span!(target: $target, $name, $($field)+))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,10 +67,10 @@ macro_rules! warn_span {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! info_span {
|
macro_rules! info_span {
|
||||||
(target: $target:expr, $name:expr) => {{
|
(target: $target:expr, $name:expr) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::info_span!(target: $target, $name))
|
$crate::Span::__from_tracing($crate::tracing::info_span!(target: $target, $name))
|
||||||
}};
|
}};
|
||||||
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::info_span!(target: $target, $name, $($field)+))
|
$crate::Span::__from_tracing($crate::tracing::info_span!(target: $target, $name, $($field)+))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,10 +78,10 @@ macro_rules! info_span {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! debug_span {
|
macro_rules! debug_span {
|
||||||
(target: $target:expr, $name:expr) => {{
|
(target: $target:expr, $name:expr) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::debug_span!(target: $target, $name))
|
$crate::Span::__from_tracing($crate::tracing::debug_span!(target: $target, $name))
|
||||||
}};
|
}};
|
||||||
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::debug_span!(target: $target, $name, $($field)+))
|
$crate::Span::__from_tracing($crate::tracing::debug_span!(target: $target, $name, $($field)+))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,9 +89,9 @@ macro_rules! debug_span {
|
|||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! trace_span {
|
macro_rules! trace_span {
|
||||||
(target: $target:expr, $name:expr) => {{
|
(target: $target:expr, $name:expr) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::trace_span!(target: $target, $name))
|
$crate::Span::__from_tracing($crate::tracing::trace_span!(target: $target, $name))
|
||||||
}};
|
}};
|
||||||
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
(target: $target:expr, $name:expr, $($field:tt)+) => {{
|
||||||
$crate::Span::__from_tracing($crate::__private_tracing::trace_span!(target: $target, $name, $($field)+))
|
$crate::Span::__from_tracing($crate::tracing::trace_span!(target: $target, $name, $($field)+))
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-logging-lib/src/runtime.rs
|
// file: crates/ksp-logging-lib/src/runtime.rs
|
||||||
// version: 13
|
// version: 16
|
||||||
|
|
||||||
use tracing_subscriber::Layer; // rust-rules: trait-import
|
use tracing_subscriber::Layer; // rust-rules: trait-import
|
||||||
use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import
|
use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import
|
||||||
@@ -228,31 +228,6 @@ pub fn initialize_with_identity(settings: &crate::LoggingSettings, identity: &cr
|
|||||||
return initialize_runtime(settings, std::option::Option::Some(identity.clone()));
|
return initialize_runtime(settings, std::option::Option::Some(identity.clone()));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn initialize_runtime(
|
|
||||||
settings: &crate::LoggingSettings,
|
|
||||||
runtime_identity: std::option::Option<crate::LoggingRuntimeIdentity>,
|
|
||||||
) -> ksp_core_lib::Result<crate::LoggingGuard> {
|
|
||||||
return prepare_runtime_with_identity(settings, runtime_identity.as_ref()).and_then(|prepared| -> ksp_core_lib::Result<crate::LoggingGuard> {
|
|
||||||
let PreparedRuntime { layers, outputs } = prepared;
|
|
||||||
let (reload_layer, reload_handle) = tracing_subscriber::reload::Layer::new(layers);
|
|
||||||
let subscriber = tracing_subscriber::registry().with(reload_layer);
|
|
||||||
let install_result = tracing::subscriber::set_global_default(subscriber);
|
|
||||||
return match install_result {
|
|
||||||
std::result::Result::Ok(()) => std::result::Result::Ok(crate::LoggingGuard {
|
|
||||||
reload_handle,
|
|
||||||
settings: settings.clone(),
|
|
||||||
runtime_identity,
|
|
||||||
outputs,
|
|
||||||
retired_dropped_lines: crate::DroppedLines::zero(),
|
|
||||||
retired_file_dropped_lines: std::collections::HashMap::new(),
|
|
||||||
}),
|
|
||||||
std::result::Result::Err(error) => std::result::Result::Err(
|
|
||||||
ksp_core_lib::Error::new(crate::ERROR_CODE_ALREADY_INITIALIZED, "the global KSP tracing subscriber is already installed").with_source(error),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces the active KSP logging settings and non-blocking outputs without reinstalling the global subscriber.
|
/// Replaces the active KSP logging settings and non-blocking outputs without reinstalling the global subscriber.
|
||||||
///
|
///
|
||||||
/// New runtime layers, writers and guards are fully prepared before the reload is attempted. If validation or preparation fails, the currently active
|
/// New runtime layers, writers and guards are fully prepared before the reload is attempted. If validation or preparation fails, the currently active
|
||||||
@@ -282,6 +257,31 @@ pub fn reinitialize(guard: &mut crate::LoggingGuard, settings: &crate::LoggingSe
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn initialize_runtime(
|
||||||
|
settings: &crate::LoggingSettings,
|
||||||
|
runtime_identity: std::option::Option<crate::LoggingRuntimeIdentity>,
|
||||||
|
) -> ksp_core_lib::Result<crate::LoggingGuard> {
|
||||||
|
return prepare_runtime_with_identity(settings, runtime_identity.as_ref()).and_then(|prepared| -> ksp_core_lib::Result<crate::LoggingGuard> {
|
||||||
|
let PreparedRuntime { layers, outputs } = prepared;
|
||||||
|
let (reload_layer, reload_handle) = tracing_subscriber::reload::Layer::new(layers);
|
||||||
|
let subscriber = tracing_subscriber::registry().with(reload_layer);
|
||||||
|
let install_result = tracing::subscriber::set_global_default(subscriber);
|
||||||
|
return match install_result {
|
||||||
|
std::result::Result::Ok(()) => std::result::Result::Ok(crate::LoggingGuard {
|
||||||
|
reload_handle,
|
||||||
|
settings: settings.clone(),
|
||||||
|
runtime_identity,
|
||||||
|
outputs,
|
||||||
|
retired_dropped_lines: crate::DroppedLines::zero(),
|
||||||
|
retired_file_dropped_lines: std::collections::HashMap::new(),
|
||||||
|
}),
|
||||||
|
std::result::Result::Err(error) => std::result::Result::Err(
|
||||||
|
ksp_core_lib::Error::new(crate::ERROR_CODE_ALREADY_INITIALIZED, "the global KSP tracing subscriber is already installed").with_source(error),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn prepare_runtime_with_identity(
|
fn prepare_runtime_with_identity(
|
||||||
settings: &crate::LoggingSettings,
|
settings: &crate::LoggingSettings,
|
||||||
runtime_identity: std::option::Option<&crate::LoggingRuntimeIdentity>,
|
runtime_identity: std::option::Option<&crate::LoggingRuntimeIdentity>,
|
||||||
@@ -315,7 +315,7 @@ fn prepare_runtime_with_identity(
|
|||||||
if output_layers.is_empty() {
|
if output_layers.is_empty() {
|
||||||
return std::result::Result::Ok(PreparedRuntime { layers: RuntimeLayers::new(), outputs });
|
return std::result::Result::Ok(PreparedRuntime { layers: RuntimeLayers::new(), outputs });
|
||||||
}
|
}
|
||||||
output_layers.insert(0, crate::domain::DomainContextLayer::new().boxed());
|
output_layers.insert(0, crate::DomainContextLayer::new().boxed());
|
||||||
let takeover_layer = build_target_filter(settings).and_then(output_layers).boxed();
|
let takeover_layer = build_target_filter(settings).and_then(output_layers).boxed();
|
||||||
return std::result::Result::Ok(PreparedRuntime { layers: vec![takeover_layer], outputs });
|
return std::result::Result::Ok(PreparedRuntime { layers: vec![takeover_layer], outputs });
|
||||||
}
|
}
|
||||||
@@ -368,7 +368,7 @@ fn build_file_output(
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
let stripped_writer = crate::writer::StripAnsiWriter::new(appender);
|
let stripped_writer = crate::StripAnsiWriter::new(appender);
|
||||||
let thread_name = format!("ksp-logging-{}", file.output_id());
|
let thread_name = format!("ksp-logging-{}", file.output_id());
|
||||||
let prepared = build_non_blocking_output(stripped_writer, thread_name.as_str(), settings.span_events(), false, false, file.format(), file.filter());
|
let prepared = build_non_blocking_output(stripped_writer, thread_name.as_str(), settings.span_events(), false, false, file.format(), file.filter());
|
||||||
let metadata = crate::RuntimeFileMetadata {
|
let metadata = crate::RuntimeFileMetadata {
|
||||||
@@ -413,7 +413,7 @@ fn build_format_layer(
|
|||||||
let span_events = map_span_events(span_events);
|
let span_events = map_span_events(span_events);
|
||||||
return match format {
|
return match format {
|
||||||
crate::LogFormat::Human => tracing_subscriber::fmt::layer()
|
crate::LogFormat::Human => tracing_subscriber::fmt::layer()
|
||||||
.with_writer(crate::writer::RouteMakeWriter::new(writer, filter.clone()))
|
.with_writer(crate::RouteMakeWriter::new(writer, filter.clone()))
|
||||||
.with_ansi(ansi)
|
.with_ansi(ansi)
|
||||||
.with_ansi_sanitization(ansi_sanitization)
|
.with_ansi_sanitization(ansi_sanitization)
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
@@ -423,7 +423,7 @@ fn build_format_layer(
|
|||||||
.boxed(),
|
.boxed(),
|
||||||
crate::LogFormat::Compact => tracing_subscriber::fmt::layer()
|
crate::LogFormat::Compact => tracing_subscriber::fmt::layer()
|
||||||
.compact()
|
.compact()
|
||||||
.with_writer(crate::writer::RouteMakeWriter::new(writer, filter.clone()))
|
.with_writer(crate::RouteMakeWriter::new(writer, filter.clone()))
|
||||||
.with_ansi(ansi)
|
.with_ansi(ansi)
|
||||||
.with_ansi_sanitization(ansi_sanitization)
|
.with_ansi_sanitization(ansi_sanitization)
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
@@ -433,7 +433,7 @@ fn build_format_layer(
|
|||||||
.boxed(),
|
.boxed(),
|
||||||
crate::LogFormat::Pretty => tracing_subscriber::fmt::layer()
|
crate::LogFormat::Pretty => tracing_subscriber::fmt::layer()
|
||||||
.pretty()
|
.pretty()
|
||||||
.with_writer(crate::writer::RouteMakeWriter::new(writer, filter.clone()))
|
.with_writer(crate::RouteMakeWriter::new(writer, filter.clone()))
|
||||||
.with_ansi(ansi)
|
.with_ansi(ansi)
|
||||||
.with_ansi_sanitization(ansi_sanitization)
|
.with_ansi_sanitization(ansi_sanitization)
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
@@ -443,7 +443,7 @@ fn build_format_layer(
|
|||||||
.boxed(),
|
.boxed(),
|
||||||
crate::LogFormat::Json => tracing_subscriber::fmt::layer()
|
crate::LogFormat::Json => tracing_subscriber::fmt::layer()
|
||||||
.json()
|
.json()
|
||||||
.with_writer(crate::writer::RouteMakeWriter::new(writer, filter.clone()))
|
.with_writer(crate::RouteMakeWriter::new(writer, filter.clone()))
|
||||||
.with_ansi(false)
|
.with_ansi(false)
|
||||||
.with_target(true)
|
.with_target(true)
|
||||||
.with_file(true)
|
.with_file(true)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-logging-lib/src/writer.rs
|
// file: crates/ksp-logging-lib/src/writer.rs
|
||||||
// version: 3
|
// version: 5
|
||||||
|
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
enum StripAnsiState {
|
enum StripAnsiState {
|
||||||
@@ -12,12 +12,14 @@ enum StripAnsiState {
|
|||||||
StringEscape,
|
StringEscape,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Crate-internal `StripAnsiWriter` state shared across the owning crate.
|
||||||
pub(crate) struct StripAnsiWriter<W> {
|
pub(crate) struct StripAnsiWriter<W> {
|
||||||
inner: W,
|
inner: W,
|
||||||
state: StripAnsiState,
|
state: StripAnsiState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> StripAnsiWriter<W> {
|
impl<W> StripAnsiWriter<W> {
|
||||||
|
/// Creates a new `StripAnsiWriter` value.
|
||||||
pub(crate) const fn new(inner: W) -> Self {
|
pub(crate) const fn new(inner: W) -> Self {
|
||||||
return Self { inner, state: StripAnsiState::Text };
|
return Self { inner, state: StripAnsiState::Text };
|
||||||
}
|
}
|
||||||
@@ -100,6 +102,7 @@ impl<W> StripAnsiWriter<W> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Crate-internal `RouteMakeWriter` state shared across the owning crate.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct RouteMakeWriter<W> {
|
pub(crate) struct RouteMakeWriter<W> {
|
||||||
inner: W,
|
inner: W,
|
||||||
@@ -107,17 +110,19 @@ pub(crate) struct RouteMakeWriter<W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<W> RouteMakeWriter<W> {
|
impl<W> RouteMakeWriter<W> {
|
||||||
|
/// Creates a new `RouteMakeWriter` value.
|
||||||
pub(crate) fn new(inner: W, filter: crate::OutputFilter) -> Self {
|
pub(crate) fn new(inner: W, filter: crate::OutputFilter) -> Self {
|
||||||
return Self { inner, filter };
|
return Self { inner, filter };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Crate-internal `RoutedWriter` variants used by the owning crate.
|
||||||
pub(crate) enum RoutedWriter<W> {
|
pub(crate) enum RoutedWriter<W> {
|
||||||
Enabled(W),
|
Enabled(W),
|
||||||
Disabled,
|
Disabled,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<W> std::io::Write for RoutedWriter<W>
|
impl<W> std::io::Write for crate::RoutedWriter<W>
|
||||||
where
|
where
|
||||||
W: std::io::Write,
|
W: std::io::Write,
|
||||||
{
|
{
|
||||||
@@ -140,17 +145,17 @@ impl<'writer, W> tracing_subscriber::fmt::MakeWriter<'writer> for RouteMakeWrite
|
|||||||
where
|
where
|
||||||
W: tracing_subscriber::fmt::MakeWriter<'writer>,
|
W: tracing_subscriber::fmt::MakeWriter<'writer>,
|
||||||
{
|
{
|
||||||
type Writer = RoutedWriter<W::Writer>;
|
type Writer = crate::RoutedWriter<W::Writer>;
|
||||||
|
|
||||||
fn make_writer(&'writer self) -> Self::Writer {
|
fn make_writer(&'writer self) -> Self::Writer {
|
||||||
return RoutedWriter::Enabled(tracing_subscriber::fmt::MakeWriter::make_writer(&self.inner));
|
return crate::RoutedWriter::Enabled(tracing_subscriber::fmt::MakeWriter::make_writer(&self.inner));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_writer_for(&'writer self, metadata: &tracing::Metadata<'_>) -> Self::Writer {
|
fn make_writer_for(&'writer self, metadata: &tracing::Metadata<'_>) -> Self::Writer {
|
||||||
if metadata_matches_filter(metadata, &self.filter) {
|
if metadata_matches_filter(metadata, &self.filter) {
|
||||||
return RoutedWriter::Enabled(tracing_subscriber::fmt::MakeWriter::make_writer_for(&self.inner, metadata));
|
return crate::RoutedWriter::Enabled(tracing_subscriber::fmt::MakeWriter::make_writer_for(&self.inner, metadata));
|
||||||
}
|
}
|
||||||
return RoutedWriter::Disabled;
|
return crate::RoutedWriter::Disabled;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +168,7 @@ fn metadata_matches_filter(metadata: &tracing::Metadata<'_>, filter: &crate::Out
|
|||||||
}) {
|
}) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return crate::domain::current_domain_matches(filter.domains());
|
return crate::current_domain_matches(filter.domains());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn level_is_enabled(level: &tracing::Level, filter: crate::LogFilterLevel) -> bool {
|
fn level_is_enabled(level: &tracing::Level, filter: crate::LogFilterLevel) -> bool {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// file: crates/ksp-logging-lib/tests/overhead.rs
|
// file: crates/ksp-logging-lib/tests/overhead.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
//! Diagnostic gross-overhead probe for the reload layer used by KSP Logging.
|
//! Diagnostic gross-overhead probe for the reload layer used by KSP Logging.
|
||||||
|
|
||||||
use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import
|
use tracing_subscriber::layer::SubscriberExt; // rust-rules: trait-import
|
||||||
|
|
||||||
const TEST_TARGET: &str = "ksp-logging-lib";
|
|
||||||
const ITERATIONS: u64 = 200_000;
|
const ITERATIONS: u64 = 200_000;
|
||||||
|
const TEST_TARGET: &str = "ksp-logging-lib";
|
||||||
|
|
||||||
fn emit_probe_events() {
|
fn emit_probe_events() {
|
||||||
for sequence in 0..ITERATIONS {
|
for sequence in 0..ITERATIONS {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// file: crates/ksp-logging-lib/tests/ownership.rs
|
// file: crates/ksp-logging-lib/tests/ownership.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
//! Integration audit ensuring KSP crates do not bypass the logging facade.
|
//! Integration audit ensuring KSP crates do not bypass the logging facade.
|
||||||
|
|
||||||
@@ -157,6 +157,11 @@ fn workspace_crates_do_not_bypass_ksp_logging_facade() {
|
|||||||
std::result::Result::Err(_) => continue,
|
std::result::Result::Err(_) => continue,
|
||||||
};
|
};
|
||||||
assert!(!source_uses_direct_path(source.as_str(), "tracing::"), "{} bypasses ksp-logging-lib via tracing", rust_file.display());
|
assert!(!source_uses_direct_path(source.as_str(), "tracing::"), "{} bypasses ksp-logging-lib via tracing", rust_file.display());
|
||||||
|
assert!(
|
||||||
|
!source_uses_direct_path(source.as_str(), "ksp_logging_lib::tracing::"),
|
||||||
|
"{} bypasses ksp-logging-lib via its hidden tracing bridge",
|
||||||
|
rust_file.display(),
|
||||||
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!source_uses_direct_path(source.as_str(), "tracing_subscriber::"),
|
!source_uses_direct_path(source.as_str(), "tracing_subscriber::"),
|
||||||
"{} bypasses ksp-logging-lib via tracing-subscriber",
|
"{} bypasses ksp-logging-lib via tracing-subscriber",
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
// file: crates/ksp-logging-lib/tests/runtime.rs
|
// file: crates/ksp-logging-lib/tests/runtime.rs
|
||||||
// version: 9
|
// version: 10
|
||||||
|
|
||||||
//! Integration tests for global initialization, takeover filtering, non-blocking outputs and hot reload.
|
//! Integration tests for global initialization, takeover filtering, non-blocking outputs and hot reload.
|
||||||
|
|
||||||
|
const EXTERNAL_TARGET: &str = "sqlx";
|
||||||
|
const JSON_KSP_TARGET: &str = "ksp-logging-json-test";
|
||||||
const LOGGING_TARGET: &str = "ksp-logging-lib";
|
const LOGGING_TARGET: &str = "ksp-logging-lib";
|
||||||
const OTHER_KSP_TARGET: &str = "ksp-store-lib";
|
const OTHER_KSP_TARGET: &str = "ksp-store-lib";
|
||||||
const JSON_KSP_TARGET: &str = "ksp-logging-json-test";
|
|
||||||
const EXTERNAL_TARGET: &str = "sqlx";
|
|
||||||
|
|
||||||
fn logging_trace_enabled() -> bool {
|
fn logging_trace_enabled() -> bool {
|
||||||
return tracing::enabled!(target: LOGGING_TARGET, tracing::Level::TRACE);
|
return tracing::enabled!(target: LOGGING_TARGET, tracing::Level::TRACE);
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
// file: crates/ksp-logging-lib/unit_tests/domain.rs
|
// file: crates/ksp-logging-lib/unit_tests/domain.rs
|
||||||
// version: 1
|
// version: 2
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn wildcard_domain_matches_with_or_without_current_domain() {
|
fn wildcard_domain_matches_with_or_without_current_domain() {
|
||||||
super::set_current_domain(std::option::Option::None);
|
super::set_current_domain(std::option::Option::None);
|
||||||
assert!(super::current_domain_matches(&["*".to_string()]));
|
assert!(crate::current_domain_matches(&["*".to_string()]));
|
||||||
super::set_current_domain(std::option::Option::Some("logging"));
|
super::set_current_domain(std::option::Option::Some("logging"));
|
||||||
assert!(super::current_domain_matches(&["*".to_string()]));
|
assert!(crate::current_domain_matches(&["*".to_string()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn named_domain_requires_current_matching_prefix() {
|
fn named_domain_requires_current_matching_prefix() {
|
||||||
super::set_current_domain(std::option::Option::None);
|
super::set_current_domain(std::option::Option::None);
|
||||||
assert!(!super::current_domain_matches(&["logging".to_string()]));
|
assert!(!crate::current_domain_matches(&["logging".to_string()]));
|
||||||
super::set_current_domain(std::option::Option::Some("logging.runtime"));
|
super::set_current_domain(std::option::Option::Some("logging.runtime"));
|
||||||
assert!(super::current_domain_matches(&["logging".to_string()]));
|
assert!(crate::current_domain_matches(&["logging".to_string()]));
|
||||||
assert!(super::current_domain_matches(&["store".to_string(), "logging.runtime".to_string()]));
|
assert!(crate::current_domain_matches(&["store".to_string(), "logging.runtime".to_string()]));
|
||||||
assert!(!super::current_domain_matches(&["store".to_string()]));
|
assert!(!crate::current_domain_matches(&["store".to_string()]));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// file: crates/ksp-logging-lib/unit_tests/writer.rs
|
// file: crates/ksp-logging-lib/unit_tests/writer.rs
|
||||||
// version: 2
|
// version: 3
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ansi_writer_strips_csi_sequences() {
|
fn ansi_writer_strips_csi_sequences() {
|
||||||
let mut writer = super::StripAnsiWriter::new(std::vec::Vec::<u8>::new());
|
let mut writer = crate::StripAnsiWriter::new(std::vec::Vec::<u8>::new());
|
||||||
let write_result = std::io::Write::write_all(&mut writer, b"before\x1b[31mred\x1b[0mafter");
|
let write_result = std::io::Write::write_all(&mut writer, b"before\x1b[31mred\x1b[0mafter");
|
||||||
assert!(write_result.is_ok());
|
assert!(write_result.is_ok());
|
||||||
assert_eq!(writer.into_inner(), b"beforeredafter");
|
assert_eq!(writer.into_inner(), b"beforeredafter");
|
||||||
@@ -11,7 +11,7 @@ fn ansi_writer_strips_csi_sequences() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ansi_writer_preserves_state_across_split_writes() {
|
fn ansi_writer_preserves_state_across_split_writes() {
|
||||||
let mut writer = super::StripAnsiWriter::new(std::vec::Vec::<u8>::new());
|
let mut writer = crate::StripAnsiWriter::new(std::vec::Vec::<u8>::new());
|
||||||
let first = std::io::Write::write_all(&mut writer, b"a\x1b[");
|
let first = std::io::Write::write_all(&mut writer, b"a\x1b[");
|
||||||
let second = std::io::Write::write_all(&mut writer, b"32mb");
|
let second = std::io::Write::write_all(&mut writer, b"32mb");
|
||||||
assert!(first.is_ok());
|
assert!(first.is_ok());
|
||||||
@@ -21,7 +21,7 @@ fn ansi_writer_preserves_state_across_split_writes() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ansi_writer_strips_osc_sequences_terminated_by_bell_or_st() {
|
fn ansi_writer_strips_osc_sequences_terminated_by_bell_or_st() {
|
||||||
let mut writer = super::StripAnsiWriter::new(std::vec::Vec::<u8>::new());
|
let mut writer = crate::StripAnsiWriter::new(std::vec::Vec::<u8>::new());
|
||||||
let first = std::io::Write::write_all(&mut writer, b"a\x1b]0;title\x07b");
|
let first = std::io::Write::write_all(&mut writer, b"a\x1b]0;title\x07b");
|
||||||
let second = std::io::Write::write_all(&mut writer, b"c\x1b]8;;https://example.invalid\x1b\\d");
|
let second = std::io::Write::write_all(&mut writer, b"c\x1b]8;;https://example.invalid\x1b\\d");
|
||||||
assert!(first.is_ok());
|
assert!(first.is_ok());
|
||||||
@@ -43,7 +43,7 @@ fn output_level_routing_covers_all_filter_levels() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn disabled_routed_writer_discards_bytes_without_error() {
|
fn disabled_routed_writer_discards_bytes_without_error() {
|
||||||
let mut writer = super::RoutedWriter::<std::vec::Vec<u8>>::Disabled;
|
let mut writer = crate::RoutedWriter::<std::vec::Vec<u8>>::Disabled;
|
||||||
let write_result = std::io::Write::write_all(&mut writer, b"discarded");
|
let write_result = std::io::Write::write_all(&mut writer, b"discarded");
|
||||||
let flush_result = std::io::Write::flush(&mut writer);
|
let flush_result = std::io::Write::flush(&mut writer);
|
||||||
assert!(write_result.is_ok());
|
assert!(write_result.is_ok());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
|
<!-- file: crates/ksp-onchain-transport-lib/README.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 9 -->
|
||||||
|
|
||||||
# `ksp-onchain-transport-lib`
|
# `ksp-onchain-transport-lib`
|
||||||
|
|
||||||
@@ -69,16 +69,19 @@ Le registre porte notamment :
|
|||||||
- remplacement historique éventuel ;
|
- remplacement historique éventuel ;
|
||||||
- release de couverture typée KSP.
|
- release de couverture typée KSP.
|
||||||
|
|
||||||
La surface typée stable après `0.2.2` contient **26 méthodes** :
|
La release stable `0.2.4` complète la surface typée des **52 méthodes courantes** :
|
||||||
|
|
||||||
```text
|
```text
|
||||||
0.2.1 foundation : 4
|
0.2.1 foundation : 4
|
||||||
0.2.2 Accounts : 5
|
0.2.2 Accounts/Tokens/Cluster : 22
|
||||||
0.2.2 Tokens : 5
|
0.2.3 Transactions : 11
|
||||||
0.2.2 Cluster : 12
|
0.2.4 Blocks/Economics : 15
|
||||||
|
total : 52
|
||||||
```
|
```
|
||||||
|
|
||||||
Les quatre canaris foundation restent `getBalance`, `getGenesisHash`, `getHealth` et `getVersion`. `0.2.2` ajoute les 22 wrappers Accounts/Tokens/Cluster affectés à `HttpRpcCoverageRelease::V0_2_2`. Les 26 méthodes Transactions/Blocks/Economics encore affectées à `0.2.3`–`0.2.4` peuvent déjà passer par l'exécuteur JSON-RPC standard générique lorsqu'un consumer fournit explicitement descriptor et paramètres JSON, mais cette surface raw/générique **ne vaut pas couverture typée**.
|
`0.2.4` ajoute les dix wrappers Blocks et les cinq wrappers Economics. Les points sensibles restent notamment `getBlock` moderne + bare encoding legacy deprecated, les quatre `transactionDetails`, les versions transaction numériques génériques, `numRewardPartitions`, `commissionBps`, les overloads de `getBlocks`, les ranges de production, les valeurs d'inflation/minimum de délégation fournies par le runtime et les `null` positionnels de `getInflationReward`.
|
||||||
|
|
||||||
|
`KSP-TRANSPORT-007` impose qu'un wrapper typé couvre toutes les possibilités RPC supportées retenues par l'audit : paramètres/options, overloads et formes legacy encore supportées, contraintes déterministes utiles et variantes de réponse pertinentes sans perte. Le réaudit final `0.2.4` agrège le réaudit 37/37 de `0.2.3` avec les 15 nouveaux wrappers et porte la preuve stable à **52/52**.
|
||||||
|
|
||||||
Les 14 méthodes historiques restent découvrables pour la compliance mais sont `Removed` et ne sont pas simulées comme appelables.
|
Les 14 méthodes historiques restent découvrables pour la compliance mais sont `Removed` et ne sont pas simulées comme appelables.
|
||||||
|
|
||||||
@@ -122,7 +125,10 @@ Deux smokes Devnet opt-in sont séparés par responsabilité :
|
|||||||
|
|
||||||
```text
|
```text
|
||||||
Transport pur : settings programmatiques -> HttpTransportPool
|
Transport pur : settings programmatiques -> HttpTransportPool
|
||||||
-> getAccountInfo/getTokenAccountsByOwner/getEpochInfo/getVoteAccounts
|
-> Accounts/Tokens/Cluster représentatifs
|
||||||
|
-> trois reads Transactions
|
||||||
|
-> getBlockHeight
|
||||||
|
-> getInflationRate/getStakeMinimumDelegation
|
||||||
|
|
||||||
Composition historique : Config -> std.transport/devnet_public -> HttpTransportPool
|
Composition historique : Config -> std.transport/devnet_public -> HttpTransportPool
|
||||||
-> getHealth/getGenesisHash/getVersion/getBalance
|
-> getHealth/getGenesisHash/getVersion/getBalance
|
||||||
@@ -138,4 +144,8 @@ Les deux sont `ignored` par défaut. Le smoke Transport appartient durablement
|
|||||||
- [`../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md`](../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md) — foundation HTTP stable ;
|
- [`../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md`](../../docs/plans/008-V0_2_1_ONCHAIN_HTTP_PLAN.md) — foundation HTTP stable ;
|
||||||
- [`../../docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md`](../../docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md) — extension typed Accounts/Tokens/Cluster ;
|
- [`../../docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md`](../../docs/plans/009-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER_PLAN.md) — extension typed Accounts/Tokens/Cluster ;
|
||||||
- [`../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md`](../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md) — matrice finale validée `0.2.2` ;
|
- [`../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md`](../../docs/validation/004-V0_2_2_HTTP_ACCOUNTS_TOKENS_CLUSTER.md) — matrice finale validée `0.2.2` ;
|
||||||
|
- [`../../docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md`](../../docs/plans/010-V0_2_3_HTTP_TRANSACTIONS_PLAN.md) — plan historique clôturé de `0.2.3` ;
|
||||||
|
- [`../../docs/validation/006-V0_2_3_HTTP_TRANSACTIONS.md`](../../docs/validation/006-V0_2_3_HTTP_TRANSACTIONS.md) — matrice finale validée `0.2.3` ;
|
||||||
|
- [`../../docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md`](../../docs/plans/011-V0_2_4_HTTP_BLOCKS_ECONOMICS_PLAN.md) — plan Blocks/Economics et compliance HTTP finale ;
|
||||||
|
- [`../../docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md`](../../docs/validation/007-V0_2_4_HTTP_FINAL_COMPLIANCE.md) — matrice finale validée `52/52 + 14/14` et audit `KSP-TRANSPORT-007` global ;
|
||||||
- [`../../config/std.transport.json`](../../config/std.transport.json) — configuration standard HTTP.
|
- [`../../config/std.transport.json`](../../config/std.transport.json) — configuration standard HTTP.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<!-- file: crates/ksp-onchain-transport-lib/USAGE.md -->
|
<!-- file: crates/ksp-onchain-transport-lib/USAGE.md -->
|
||||||
<!-- version: 5 -->
|
<!-- version: 9 -->
|
||||||
|
|
||||||
# Utilisation de `ksp-onchain-transport-lib`
|
# Utilisation de `ksp-onchain-transport-lib`
|
||||||
|
|
||||||
@@ -96,7 +96,34 @@ let epoch = pool.get_epoch_info(&role, None).await;
|
|||||||
let vote_accounts = pool.get_vote_accounts(&role, None).await;
|
let vote_accounts = pool.get_vote_accounts(&role, None).await;
|
||||||
```
|
```
|
||||||
|
|
||||||
La surface stable `0.2.2` contient 26 wrappers typés au total : 4 foundation + 5 Accounts + 5 Tokens + 12 Cluster. Les DTOs Transport conservent les `null`, options et formes wire : données Account encodées/`jsonParsed`, `TokenAmount.uiAmount`, contexte RPC, nodes, epoch, leader schedule et vote accounts. Aucun décodage Program/SPL métier n'est effectué ici.
|
La release stable `0.2.4` contient les **52 wrappers typés courants** : 4 foundation + 22 Accounts/Tokens/Cluster + 11 Transactions + 10 Blocks + 5 Economics. Les DTOs Transport conservent les `null`, options, overloads et formes wire sans décodage Program/SPL métier.
|
||||||
|
|
||||||
|
Exemples Transaction représentatifs :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let context = ksp_onchain_transport_lib::SolanaContextConfig::new(
|
||||||
|
std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized),
|
||||||
|
std::option::Option::None,
|
||||||
|
);
|
||||||
|
let latest = pool
|
||||||
|
.get_latest_blockhash(&role, std::option::Option::Some(&context))
|
||||||
|
.await;
|
||||||
|
let transaction_count = pool
|
||||||
|
.get_transaction_count(&role, std::option::Option::Some(&context))
|
||||||
|
.await;
|
||||||
|
```
|
||||||
|
|
||||||
|
`getTransaction` expose la config moderne complète et une forme bare-encoding legacy séparée et deprecated. `requestAirdrop` et `sendTransaction` sont des write submissions : elles utilisent la protection centrale `NeverAfterDispatch`. `simulateTransaction` reste une simulation retry-safe et conserve son résultat riche sans introduire de décodage Program.
|
||||||
|
|
||||||
|
Exemples Blocks/Economics représentatifs :
|
||||||
|
|
||||||
|
```rust
|
||||||
|
let block_height = pool.get_block_height(&role, Some(&context)).await;
|
||||||
|
let inflation_rate = pool.get_inflation_rate(&role).await;
|
||||||
|
let stake_minimum = pool.get_stake_minimum_delegation(&role, Some(&context)).await;
|
||||||
|
```
|
||||||
|
|
||||||
|
`getBlock` possède également une forme bare-encoding legacy séparée et deprecated. Les valeurs Economics restent celles du runtime : le consumer ne doit pas supposer localement un taux d'inflation ou un minimum de délégation constant.
|
||||||
|
|
||||||
## 4. Exécution JSON-RPC standard générique
|
## 4. Exécution JSON-RPC standard générique
|
||||||
|
|
||||||
@@ -108,7 +135,7 @@ if let Some(descriptor) = ksp_onchain_transport_lib::find_http_rpc_method("getSl
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Cette API retourne un `serde_json::Value`. Elle est utile pour les consumers techniques et pour préparer les futures surfaces typées, mais elle ne remplace pas le wrapper typé d'une méthode dans la matrice de couverture KSP.
|
Cette API retourne un `serde_json::Value`. Elle reste utile pour les extensions provider, les diagnostics et les méthodes hors registre standard, mais elle ne remplace jamais le wrapper typé d'une méthode HTTP standard désormais couverte par KSP.
|
||||||
|
|
||||||
Avant exécution, `ensure_runtime_supported()` est appliqué. Une méthode historique `Removed` retourne `ERROR_CODE_METHOD_REMOVED` au lieu d'émettre un appel réseau fictif.
|
Avant exécution, `ensure_runtime_supported()` est appliqué. Une méthode historique `Removed` retourne `ERROR_CODE_METHOD_REMOVED` au lieu d'émettre un appel réseau fictif.
|
||||||
|
|
||||||
@@ -155,13 +182,13 @@ La configuration standard route les événements `info` de Transport vers un fic
|
|||||||
|
|
||||||
## 9. Smokes Devnet opt-in
|
## 9. Smokes Devnet opt-in
|
||||||
|
|
||||||
Le smoke **Transport pur** construit ses settings programmatiquement et exerce un sous-ensemble représentatif de `0.2.2` :
|
Le smoke **Transport pur** construit ses settings programmatiquement et exerce un sous-ensemble représentatif d'Accounts/Tokens/Cluster, trois reads Transactions, puis des reads Blocks/Economics de la release stable `0.2.4` :
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo test -p ksp-onchain-transport-lib --test transport_devnet_smoke -- --ignored --nocapture
|
cargo test -p ksp-onchain-transport-lib --test transport_devnet_smoke -- --ignored --nocapture
|
||||||
```
|
```
|
||||||
|
|
||||||
Il appelle `getAccountInfo`, `getTokenAccountsByOwner`, `getEpochInfo` et `getVoteAccounts`. La branche Token suit la forme Devnet documentée : owner Pubkey ordinaire de l'exemple officiel, selector `programId` avec l'ID canonique du programme SPL Token, puis config explicite `commitment: finalized` + `encoding: jsonParsed`. Une réponse vide reste acceptable : le smoke valide ainsi la route Token sans dépendre de la persistance d'un mint ou d'un token account Devnet particulier.
|
Il appelle `getAccountInfo`, `getTokenAccountsByOwner`, `getEpochInfo`, `getVoteAccounts`, puis `getLatestBlockhash`, `isBlockhashValid`, `getTransactionCount`, `getBlockHeight`, `getInflationRate` et `getStakeMinimumDelegation`. La branche Token suit la forme Devnet documentée : owner Pubkey ordinaire de l'exemple officiel, selector `programId` avec l'ID canonique du programme SPL Token, puis config explicite `commitment: finalized` + `encoding: jsonParsed`. Une réponse vide reste acceptable. La branche Transaction reste read-only : elle ne déclenche ni airdrop ni soumission de transaction et ne remplace pas les fixtures déterministes couvrant les 11 wrappers.
|
||||||
|
|
||||||
Le smoke historique de **composition Config -> Transport** reste également disponible :
|
Le smoke historique de **composition Config -> Transport** reste également disponible :
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
[
|
||||||
|
{"commitment":[1,2,3,4],"totalStake":1000000000},
|
||||||
|
{"commitment":null,"totalStake":1000000000}
|
||||||
|
]
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"byIdentity": {
|
||||||
|
"11111111111111111111111111111111": [8,7],
|
||||||
|
"Vote111111111111111111111111111111111111111": [4,4]
|
||||||
|
},
|
||||||
|
"range": {"firstSlot":430000000,"lastSlot":430000099}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"previousBlockhash":"previous-a",
|
||||||
|
"blockhash":"block-a",
|
||||||
|
"parentSlot":1,
|
||||||
|
"blockTime":null,
|
||||||
|
"blockHeight":null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"previousBlockhash":"previous-b",
|
||||||
|
"blockhash":"block-b",
|
||||||
|
"parentSlot":2,
|
||||||
|
"transactions":null,
|
||||||
|
"signatures":null,
|
||||||
|
"rewards":null,
|
||||||
|
"numRewardPartitions":null,
|
||||||
|
"blockTime":null,
|
||||||
|
"blockHeight":null
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"previousBlockhash":"previous-blockhash-fixture",
|
||||||
|
"blockhash":"blockhash-fixture",
|
||||||
|
"parentSlot":430000122,
|
||||||
|
"transactions":[
|
||||||
|
{
|
||||||
|
"transaction":{"signatures":["signature-fixture"],"message":{"accountKeys":[],"recentBlockhash":"recent-fixture","instructions":[]}},
|
||||||
|
"meta":{"err":null,"fee":5000},
|
||||||
|
"version":1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"transaction":["AQID","base64"],
|
||||||
|
"meta":null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"rewards":[
|
||||||
|
{
|
||||||
|
"pubkey":"11111111111111111111111111111111",
|
||||||
|
"lamports":2500,
|
||||||
|
"postBalance":1000002500,
|
||||||
|
"rewardType":"Fee",
|
||||||
|
"commission":null,
|
||||||
|
"commissionBps":1234
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pubkey":"Vote111111111111111111111111111111111111111",
|
||||||
|
"lamports":1250,
|
||||||
|
"postBalance":2000001250,
|
||||||
|
"rewardType":"Voting",
|
||||||
|
"commission":5
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"numRewardPartitions":4,
|
||||||
|
"blockTime":1787072400,
|
||||||
|
"blockHeight":410000000
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"slot": 430000123,
|
||||||
|
"blockTime": 1787000000,
|
||||||
|
"transaction": ["AQIDBAUGBwg=", "base64"],
|
||||||
|
"meta": {
|
||||||
|
"err": null,
|
||||||
|
"status": {"Ok": null},
|
||||||
|
"fee": 5000,
|
||||||
|
"preBalances": [10000],
|
||||||
|
"postBalances": [5000]
|
||||||
|
},
|
||||||
|
"version": 0,
|
||||||
|
"transactionIndex": 7
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slot": 430000100,
|
||||||
|
"blockTime": null,
|
||||||
|
"transaction": "3Bxs4NN8M2Yn4TLb",
|
||||||
|
"meta": null,
|
||||||
|
"version": "legacy",
|
||||||
|
"transactionIndex": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"slot": 430000050,
|
||||||
|
"blockTime": null,
|
||||||
|
"transaction": {
|
||||||
|
"signatures": [],
|
||||||
|
"message": {
|
||||||
|
"accountKeys": [],
|
||||||
|
"recentBlockhash": "11111111111111111111111111111111",
|
||||||
|
"instructions": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
"3Bxs4NN8M2Yn4TLb",
|
||||||
|
["AQIDBAUGBwg=", "base64"],
|
||||||
|
{
|
||||||
|
"signatures": ["5h6xBEauJ3PK6SWQkgLxHcQpWyRZyG7c4pUpK9fd2B2B7hRQ1S6F9J7BqvQPEmxQxK4DThfYF5f5f5f5f5f5f5f5"],
|
||||||
|
"message": {
|
||||||
|
"accountKeys": [],
|
||||||
|
"recentBlockhash": "11111111111111111111111111111111",
|
||||||
|
"instructions": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"transactions":[{"transaction":{"signatures":["sig-accounts"],"accountKeys":[{"pubkey":"11111111111111111111111111111111","writable":true,"signer":true,"source":"transaction"}]},"meta":{"err":null,"status":{"Ok":null},"fee":5000,"preBalances":[10],"postBalances":[5]},"version":1}]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"rewards":[{"pubkey":"11111111111111111111111111111111","lamports":2500,"postBalance":1000002500,"rewardType":"Fee","commission":null,"commissionBps":1234},{"pubkey":"Vote111111111111111111111111111111111111111","lamports":1250,"postBalance":2000001250,"rewardType":"Voting","commission":5}],"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"transactions":[{"transaction":["3BxsFixtureBase58","base58"],"meta":{"err":null,"fee":5000},"version":"legacy"}]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"rewards":[{"pubkey":"11111111111111111111111111111111","lamports":2500,"postBalance":1000002500,"rewardType":"Fee","commission":null,"commissionBps":1234},{"pubkey":"Vote111111111111111111111111111111111111111","lamports":1250,"postBalance":2000001250,"rewardType":"Voting","commission":5}],"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"transactions":[{"transaction":["AQID","base64"],"meta":{"err":null,"fee":5000},"version":"legacy"}]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"rewards":[{"pubkey":"11111111111111111111111111111111","lamports":2500,"postBalance":1000002500,"rewardType":"Fee","commission":null,"commissionBps":1234},{"pubkey":"Vote111111111111111111111111111111111111111","lamports":1250,"postBalance":2000001250,"rewardType":"Voting","commission":5}],"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"transactions":[{"transaction":"3BxsFixtureLegacyBinary","meta":{"err":null,"fee":5000},"version":"legacy"}]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","error":{"code":-32004,"message":"Block not available for slot 430000123"},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","error":{"code":-32015,"message":"Transaction version (1) is not supported by the requesting client. Please try the request again with maxSupportedTransactionVersion 1"},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"rewards":[{"pubkey":"11111111111111111111111111111111","lamports":2500,"postBalance":1000002500,"rewardType":"Fee","commission":null,"commissionBps":1234},{"pubkey":"Vote111111111111111111111111111111111111111","lamports":1250,"postBalance":2000001250,"rewardType":"Voting","commission":5}],"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"transactions":[{"transaction":{"signatures":["sig-v1"],"message":{"accountKeys":[],"recentBlockhash":"recent-v1","instructions":[],"transactionConfig":{"priorityFee":1,"computeUnitLimit":200000}}},"meta":{"err":null,"fee":5000,"status":{"Ok":null},"rewards":[{"pubkey":"11111111111111111111111111111111","lamports":1,"postBalance":2,"rewardType":"Fee","commission":null,"commissionBps":1234}]},"version":1},{"transaction":["AQID","base64"],"meta":null}]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"transactions":[{"transaction":{"signatures":["sig-full-no-rewards"],"message":{"accountKeys":[],"recentBlockhash":"recent-full-no-rewards","instructions":[]}},"meta":{"err":null,"fee":5000,"rewards":null},"version":1}]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"rewards":[{"pubkey":"11111111111111111111111111111111","lamports":2500,"postBalance":1000002500,"rewardType":"Fee","commission":null,"commissionBps":1234},{"pubkey":"Vote111111111111111111111111111111111111111","lamports":1250,"postBalance":2000001250,"rewardType":"Voting","commission":5}],"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"transactions":[{"transaction":{"signatures":["sig-json"],"message":{"accountKeys":[],"recentBlockhash":"recent-json","instructions":[]}},"meta":{"err":null,"fee":5000},"version":"legacy"}]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"rewards":[{"pubkey":"11111111111111111111111111111111","lamports":2500,"postBalance":1000002500,"rewardType":"Fee","commission":null,"commissionBps":1234},{"pubkey":"Vote111111111111111111111111111111111111111","lamports":1250,"postBalance":2000001250,"rewardType":"Voting","commission":5}],"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"transactions":[{"transaction":{"signatures":["sig-parsed"],"message":{"accountKeys":[],"recentBlockhash":"recent-parsed","instructions":[]}},"meta":{"err":null,"fee":5000},"version":"legacy"}]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":null,"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-b","blockhash":"block-b","parentSlot":2,"transactions":null,"signatures":null,"rewards":null,"numRewardPartitions":null,"blockTime":null,"blockHeight":null},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-a","blockhash":"block-a","parentSlot":1,"blockTime":null,"blockHeight":null},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"previousBlockhash":"previous-blockhash-fixture","blockhash":"blockhash-fixture","parentSlot":430000122,"numRewardPartitions":4,"blockTime":1787072400,"blockHeight":410000000,"signatures":["sig-a","sig-b"]},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"commitment":[1,2,3,4],"totalStake":1000000000},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":410000000,"id":1}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"jsonrpc":"2.0",
|
||||||
|
"result":{
|
||||||
|
"context":{"slot":430000123,"apiVersion":"4.2.1"},
|
||||||
|
"value":{
|
||||||
|
"byIdentity":{"not-a-pubkey":[8,7]},
|
||||||
|
"range":{"firstSlot":430000000,"lastSlot":430000099}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id":1
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"jsonrpc":"2.0",
|
||||||
|
"result":{
|
||||||
|
"context":{"slot":430000123,"apiVersion":"4.2.1"},
|
||||||
|
"value":{
|
||||||
|
"byIdentity":{"11111111111111111111111111111111":[8,7]},
|
||||||
|
"range":{"firstSlot":430000000,"lastSlot":430000099}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"id":1
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":null,"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":1787072400,"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":[],"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":[430000100,430000103,430000109],"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":[],"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":[430000200,430000201,430000205],"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid param: unable to decode message"},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"context":{"apiVersion":"4.2.1","slot":431000002},"value":null},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"context":{"apiVersion":"4.2.1","slot":431000001},"value":5000},"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":250000,"id":1}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"jsonrpc":"2.0","result":{"initial":0.07123456789,"terminal":0.0123456789,"taper":0.2718281828,"foundation":0.0042,"foundationTerm":6.25},"id":1}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user