# Utilisation de `ksp-core-lib` ## Utiliser le type d'erreur commun Une crate KSP définit des codes stables puis retourne `ksp_core_lib::Result` : ```rust const ERROR_CODE_LOAD_FAILED: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("store", "load_failed"); fn load_value() -> ksp_core_lib::Result { let error = ksp_core_lib::Error::new( ERROR_CODE_LOAD_FAILED, "unable to load value", ) .with_context("operation", "load_value"); return std::result::Result::Err(error); } ``` Le domaine et le code sont accessibles séparément : ```rust let code = ERROR_CODE_LOAD_FAILED; assert_eq!(code.domain(), "store"); assert_eq!(code.code(), "load_failed"); ``` Le message et les champs de contexte restent accessibles sans parser `Display` : ```rust let error = ksp_core_lib::Error::new( ERROR_CODE_LOAD_FAILED, "unable to load value", ) .with_context("component", "postgres") .with_context("operation", "load_value"); assert_eq!(error.code(), ERROR_CODE_LOAD_FAILED); assert_eq!(error.message(), "unable to load value"); assert_eq!(error.context()[0].key(), "component"); assert_eq!(error.context()[0].value(), "postgres"); ``` Un `ErrorContext` peut aussi être construit indépendamment lorsqu’un caller prépare explicitement son contexte : ```rust let context = ksp_core_lib::ErrorContext::new( "operation", "load_value", ); assert_eq!(context.key(), "operation"); assert_eq!(context.value(), "load_value"); ``` Les valeurs de contexte doivent être sûres à exposer. Ne pas y placer de secret, credential, seed, key material, URL contenant une clé API ou payload brut volumineux. ## Conserver une erreur source Une erreur externe compatible `Send + Sync + 'static` peut rester dans la chaîne standard : ```rust #[derive(Debug)] struct SourceError; impl std::fmt::Display for SourceError { fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { return formatter.write_str("source failure"); } } impl std::error::Error for SourceError {} let error = ksp_core_lib::Error::new( ERROR_CODE_LOAD_FAILED, "unable to load value", ) .with_source(SourceError); let source = std::error::Error::source(&error); assert!(source.is_some()); ``` La source sert au chaînage d'erreurs ; son contenu ne doit pas être recopié sans contrôle dans un contexte ou un log public. ## Utiliser `Pubkey` Core réexporte la primitive Solana utilisée par les contrats KSP : ```rust let system = ksp_core_lib::Pubkey::from_str_const( ksp_core_lib::PRGID_SOLANA_SYSTEM, ); assert_eq!(system, ksp_core_lib::PRGIDPK_SOLANA_SYSTEM); ``` Une crate consommatrice peut donc utiliser `ksp_core_lib::Pubkey` dans sa propre API sans dépendre directement de `solana-pubkey` lorsque Core est déjà le propriétaire architectural de cette primitive. ## Utiliser les constantes Program ID Chaque Program ID Core possède une forme texte et une forme typée : ```rust let system_text: &str = ksp_core_lib::PRGID_SOLANA_SYSTEM; let system_pubkey: ksp_core_lib::Pubkey = ksp_core_lib::PRGIDPK_SOLANA_SYSTEM; assert_eq!( system_pubkey, ksp_core_lib::Pubkey::from_str_const(system_text), ); ``` Les constantes `PRGID_*` sont utiles pour les wires, diagnostics sûrs ou comparaisons texte. Les constantes `PRGIDPK_*` sont préférées dès qu'un contrat manipule une adresse Solana typée. ## Déclarer une paire texte / `Pubkey` `declare_program_id!` permet à une couche KSP propriétaire d'un Program ID de déclarer les deux représentations depuis une seule valeur Base58 : ```rust ksp_core_lib::declare_program_id!( PRGID_EXAMPLE, PRGIDPK_EXAMPLE, "11111111111111111111111111111111" ); assert_eq!(PRGID_EXAMPLE, ksp_core_lib::PRGID_SOLANA_SYSTEM); assert_eq!(PRGIDPK_EXAMPLE, ksp_core_lib::PRGIDPK_SOLANA_SYSTEM); ``` La macro ne signifie pas que toute nouvelle constante doit être ajoutée au registre Core. La couche propriétaire du domaine décide où vit le nouveau Program ID. ## Parcourir le registre canonique `entries()` retourne le registre complet en ordre déterministe : ```rust for entry in ksp_core_lib::entries() { let code = entry.code(); let name = entry.name(); let text = entry.program_id(); let pubkey = entry.pubkey(); let domain = entry.domain(); let family = entry.family(); let protocol = entry.protocol(); let subfamily = entry.subfamily(); let program_version = entry.program_version(); let kind = entry.kind(); let _ = ( code, name, text, pubkey, domain, family, protocol, subfamily, program_version, kind, ); } ``` `native_program_ids()` fournit la vue des Program IDs Solana fondamentaux actuellement enregistrés : ```rust let count = ksp_core_lib::native_program_ids().count(); assert_eq!(count, 18); ``` ## Rechercher une entrée Recherche par représentation Base58 : ```rust let entry = ksp_core_lib::find_program_id( ksp_core_lib::PRGID_SOLANA_SYSTEM, ); let entry = match entry { std::option::Option::Some(value) => value, std::option::Option::None => return, }; assert_eq!(entry.code(), "solana.system"); ``` Recherche par `Pubkey` : ```rust let entry = ksp_core_lib::find_program_pubkey( &ksp_core_lib::PRGIDPK_SOLANA_VOTE, ); assert!(entry.is_some()); ``` Une recherche inconnue retourne `None`; le registre n'invente pas une entrée générique. ## Filtrer par axe simple Les helpers spécialisés conviennent aux filtres simples : ```rust let loaders = ksp_core_lib::program_ids_by_family("loader"); for loader in loaders { assert_eq!(loader.family(), "loader"); } ``` Les vues disponibles peuvent être consommées directement : ```rust let solana_entries = ksp_core_lib::program_ids_by_domain("solana").count(); let loaders = ksp_core_lib::program_ids_by_family("loader").count(); let solana_protocol = ksp_core_lib::program_ids_by_protocol("solana").count(); assert_eq!(solana_entries, 18); assert_eq!(loaders, 5); assert_eq!(solana_protocol, 18); ``` ## Combiner plusieurs axes `ProgramIdFilter` compose les contraintes sans allocation de collection intermédiaire : ```rust let filter = ksp_core_lib::ProgramIdFilter::new() .with_domain("solana") .with_family("loader") .with_protocol("solana") .with_subfamily("bpf") .with_program_version("v2") .with_kind(ksp_core_lib::ProgramIdKind::Loader); for entry in ksp_core_lib::program_ids(filter) { assert_eq!(entry.domain(), "solana"); assert_eq!(entry.family(), "loader"); assert_eq!(entry.protocol(), "solana"); assert_eq!(entry.subfamily(), std::option::Option::Some("bpf")); assert_eq!(entry.program_version(), std::option::Option::Some("v2")); assert_eq!(entry.kind(), ksp_core_lib::ProgramIdKind::Loader); } ``` Un `ProgramIdFilter::new()` vide correspond à toutes les entrées du registre. ## Choisir entre texte, `Pubkey` et descriptor Utiliser : ```text PRGID_* / &str pour une représentation Base58 canonique PRGIDPK_* / Pubkey pour un contrat Solana typé ProgramIdEntry lorsqu'il faut aussi la taxonomie KSP ``` Ne pas reparcourir les constantes ou reconstruire une taxonomie parallèle dans une crate consommatrice lorsque le registre Core fournit déjà l'information requise.