v0.2.14-pre.004
This commit is contained in:
@@ -1,11 +1,11 @@
|
||||
<!-- file: crates/ksp-program-api/README.md -->
|
||||
<!-- version: 2 -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# ksp-program-api
|
||||
|
||||
`ksp-program-api` est la façade publique ouverte du domaine Program KSP. Elle est destinée aux implémentations officielles futures comme aux crates Program externes et ne possède pas les implémentations concrètes.
|
||||
|
||||
À partir de `0.2.14-pre.003`, la foundation expose les types Core/Interface retenus ainsi que le vocabulaire minimal de reconnaissance et d'outcome de décodage. Le trait decoder reste réservé à la tranche suivante.
|
||||
À partir de `0.2.14-pre.004`, la foundation expose les types Core/Interface retenus, le vocabulaire minimal de reconnaissance/décodage et le trait instruction-only `ProgramInstructionDecoder`.
|
||||
|
||||
## Ownership
|
||||
|
||||
@@ -35,18 +35,19 @@ ProgramAccountMeta
|
||||
ProgramInstruction
|
||||
```
|
||||
|
||||
Program API possède désormais :
|
||||
Program API possède :
|
||||
|
||||
```text
|
||||
ProgramInstructionRecognition
|
||||
ProgramInstructionDecodeOutcome<Decoded>
|
||||
ProgramInstructionDecoder
|
||||
```
|
||||
|
||||
`ksp-program-api` réexporte l'ensemble depuis son crate-root pour offrir une façade de consommation stable sans dupliquer les types Core/Interface ni transférer leur ownership.
|
||||
`ksp-program-api` réexporte l'ensemble depuis son crate-root. Aucun module interne n'est public.
|
||||
|
||||
## Recognition
|
||||
|
||||
`ProgramInstructionRecognition` est `#[non_exhaustive]` et possède trois états :
|
||||
`ProgramInstructionRecognition` est `#[non_exhaustive]` :
|
||||
|
||||
```text
|
||||
NoMatch l'implémentation ne revendique pas l'instruction
|
||||
@@ -58,20 +59,35 @@ Cette reconnaissance ne contient aucun score, priorité, proof, confidence, disc
|
||||
|
||||
## Decode outcome
|
||||
|
||||
`ProgramInstructionDecodeOutcome<Decoded>` est également `#[non_exhaustive]` :
|
||||
`ProgramInstructionDecodeOutcome<Decoded>` est `#[non_exhaustive]` :
|
||||
|
||||
```text
|
||||
Decoded(Decoded) valeur typée possédée par l'implémentation
|
||||
Unsupported instruction connue mais non supportée par cette capability
|
||||
```
|
||||
|
||||
Un futur `decode(...)` utilisera le `Result` Core : une erreur de validation/décodage restera donc `Err`, sans variante parallèle `Failed`.
|
||||
Les échecs réels passent par le `Result` Core. Il n'existe aucune variante parallèle `Failed`.
|
||||
|
||||
Le `Debug` de l'outcome n'impose pas `Decoded: Debug` et n'affiche jamais la valeur `Decoded`. Il produit uniquement le nom sûr de l'état (`Decoded` ou `Unsupported`).
|
||||
Le `Debug` de l'outcome n'impose pas `Decoded: Debug` et n'affiche jamais la valeur `Decoded`.
|
||||
|
||||
## Decoder instruction-only
|
||||
|
||||
`ProgramInstructionDecoder` est un trait ouvert `Send + Sync` :
|
||||
|
||||
```text
|
||||
type Decoded
|
||||
program_ids(&self) -> &[Pubkey]
|
||||
recognize(&self, &ProgramInstruction) -> ProgramInstructionRecognition
|
||||
decode(&self, &ProgramInstruction) -> Result<ProgramInstructionDecodeOutcome<Self::Decoded>>
|
||||
```
|
||||
|
||||
Le type `Decoded` est possédé par l'implémentation. Un decoder externe peut utiliser un `Pubkey` absent du registry Core : aucun enum central, `Any`, JSON ou descriptor global n'est requis pour déclarer un Program.
|
||||
|
||||
Le trait ne fournit aucun default method et ne promet pas de registry dyn hétérogène. `program_ids` et `recognize` servent à la sélection explicite ; `decode` traite une instruction déjà sélectionnée pour le decoder.
|
||||
|
||||
## Surface actuelle
|
||||
|
||||
La façade `pre.003` expose :
|
||||
La façade `pre.004` expose :
|
||||
|
||||
```text
|
||||
Error
|
||||
@@ -83,13 +99,6 @@ ProgramAccountMeta
|
||||
ProgramInstruction
|
||||
ProgramInstructionRecognition
|
||||
ProgramInstructionDecodeOutcome<Decoded>
|
||||
```
|
||||
|
||||
Aucun module interne n'est public.
|
||||
|
||||
Le contrat suivant reste réservé à `pre.004` :
|
||||
|
||||
```text
|
||||
ProgramInstructionDecoder
|
||||
```
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!-- file: crates/ksp-program-api/USAGE.md -->
|
||||
<!-- version: 2 -->
|
||||
<!-- version: 3 -->
|
||||
|
||||
# Usage de ksp-program-api
|
||||
|
||||
Cette page décrit la surface publique disponible à partir de `0.2.14-pre.003`. Utiliser uniquement les exports du crate-root ; aucun module interne ne fait partie du contrat consommable.
|
||||
Cette page décrit la surface publique disponible à partir de `0.2.14-pre.004`. Utiliser uniquement les exports du crate-root ; aucun module interne ne fait partie du contrat consommable.
|
||||
|
||||
## Construire un input Program avec la façade
|
||||
|
||||
@@ -21,12 +21,70 @@ let instruction = ksp_program_api::ProgramInstruction::try_new(
|
||||
assert!(instruction.is_ok());
|
||||
```
|
||||
|
||||
`Pubkey`, `ProgramAccountMeta` et `ProgramInstruction` conservent leur ownership Core/Interface. Program API fournit seulement une façade cohérente aux futures implémentations de capability Program.
|
||||
`Pubkey`, `ProgramAccountMeta` et `ProgramInstruction` conservent leur ownership Core/Interface.
|
||||
|
||||
## Représenter une reconnaissance
|
||||
## Implémenter un decoder externe
|
||||
|
||||
Le type décodé reste entièrement possédé par la crate d'implémentation :
|
||||
|
||||
```rust
|
||||
let recognition = ksp_program_api::ProgramInstructionRecognition::ProgramMatch;
|
||||
struct ExternalDecodedInstruction {
|
||||
opcode: u8,
|
||||
}
|
||||
|
||||
struct ExternalDecoder {
|
||||
program_ids: [ksp_program_api::Pubkey; 1],
|
||||
}
|
||||
|
||||
impl ksp_program_api::ProgramInstructionDecoder for ExternalDecoder {
|
||||
type Decoded = ExternalDecodedInstruction;
|
||||
|
||||
fn program_ids(&self) -> &[ksp_program_api::Pubkey] {
|
||||
return &self.program_ids;
|
||||
}
|
||||
|
||||
fn recognize(
|
||||
&self,
|
||||
instruction: &ksp_program_api::ProgramInstruction,
|
||||
) -> ksp_program_api::ProgramInstructionRecognition {
|
||||
if instruction.program_id() != &self.program_ids[0] {
|
||||
return ksp_program_api::ProgramInstructionRecognition::NoMatch;
|
||||
}
|
||||
return ksp_program_api::ProgramInstructionRecognition::ProgramMatch;
|
||||
}
|
||||
|
||||
fn decode(
|
||||
&self,
|
||||
instruction: &ksp_program_api::ProgramInstruction,
|
||||
) -> ksp_program_api::Result<ksp_program_api::ProgramInstructionDecodeOutcome<Self::Decoded>> {
|
||||
let opcode = match instruction.data().first() {
|
||||
std::option::Option::Some(value) => *value,
|
||||
std::option::Option::None => {
|
||||
return std::result::Result::Ok(
|
||||
ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported,
|
||||
);
|
||||
}
|
||||
};
|
||||
return std::result::Result::Ok(
|
||||
ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(
|
||||
ExternalDecodedInstruction { opcode },
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Aucun `ksp-program-lib`, enum centrale, `Any`, JSON ou codec n'est nécessaire. Le Program ID peut être un `Pubkey` opaque non enregistré par Core.
|
||||
|
||||
## Sélection explicite
|
||||
|
||||
La sélection reste distincte du décodage :
|
||||
|
||||
```rust
|
||||
let recognition = ksp_program_api::ProgramInstructionDecoder::recognize(
|
||||
&decoder,
|
||||
&instruction,
|
||||
);
|
||||
|
||||
match recognition {
|
||||
ksp_program_api::ProgramInstructionRecognition::NoMatch => {}
|
||||
@@ -36,35 +94,30 @@ match recognition {
|
||||
}
|
||||
```
|
||||
|
||||
Le wildcard est volontaire : l'enum est `#[non_exhaustive]` afin de ne pas transformer la foundation en vocabulaire fermé pour toujours.
|
||||
L'enum est `#[non_exhaustive]`. `decode` n'est pas un substitut à `recognize` : il traite une instruction déjà sélectionnée pour le decoder.
|
||||
|
||||
## Représenter un outcome de décodage
|
||||
|
||||
Le type décodé reste possédé par l'implémentation :
|
||||
## Outcome et erreur
|
||||
|
||||
```rust
|
||||
struct ExternalDecodedInstruction {
|
||||
opcode: u8,
|
||||
}
|
||||
|
||||
let outcome = ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(
|
||||
ExternalDecodedInstruction { opcode: 7_u8 },
|
||||
);
|
||||
let outcome = ksp_program_api::ProgramInstructionDecoder::decode(
|
||||
&decoder,
|
||||
&instruction,
|
||||
)?;
|
||||
|
||||
match outcome {
|
||||
ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(value) => {
|
||||
assert_eq!(value.opcode, 7_u8);
|
||||
let _opcode = value.opcode;
|
||||
}
|
||||
ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported => {}
|
||||
_ => {}
|
||||
}
|
||||
```
|
||||
|
||||
Aucun `Any`, JSON ou enum centrale n'est nécessaire pour transporter ce type.
|
||||
Une erreur réelle est un `Err(ksp_program_api::Error)`. `Unsupported` n'est pas une deuxième forme d'erreur : il indique qu'une instruction reconnue n'est volontairement pas décodée par cette capability.
|
||||
|
||||
## Debug sûr
|
||||
|
||||
`ProgramInstructionDecodeOutcome<Decoded>` possède un `Debug` volontairement opaque sur la valeur décodée :
|
||||
`ProgramInstructionDecodeOutcome<Decoded>` possède un `Debug` volontairement opaque :
|
||||
|
||||
```rust
|
||||
struct SecretDecoded;
|
||||
@@ -73,34 +126,22 @@ let outcome = ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(SecretDe
|
||||
assert_eq!(std::format!("{outcome:?}"), "Decoded");
|
||||
```
|
||||
|
||||
`SecretDecoded` n'a même pas besoin d'implémenter `Debug`. Cela empêche un diagnostic générique de rendre accidentellement un payload externe.
|
||||
`SecretDecoded` n'a pas besoin d'implémenter `Debug` et sa valeur n'est jamais rendue par l'outcome.
|
||||
|
||||
## Utiliser le contrat d'erreur commun
|
||||
## Hors surface `pre.004`
|
||||
|
||||
Les types d'erreur Core restent disponibles depuis la façade :
|
||||
|
||||
```rust
|
||||
fn forward_result(
|
||||
value: ksp_program_api::Result<ksp_program_api::ProgramInstruction>,
|
||||
) -> ksp_program_api::Result<ksp_program_api::ProgramInstruction> {
|
||||
return value;
|
||||
}
|
||||
```
|
||||
|
||||
Le futur `decode(...)` utilisera ce `Result`. Une erreur sera donc `Err(...)`, tandis que `Unsupported` signifie une instruction connue volontairement non prise en charge.
|
||||
|
||||
## Ce que `pre.003` ne fournit pas
|
||||
|
||||
Il n'existe encore aucun :
|
||||
Il n'existe toujours aucun :
|
||||
|
||||
```text
|
||||
ProgramInstructionDecoder
|
||||
program_ids(...)
|
||||
recognize(...) sur un trait
|
||||
decode(...) sur un trait
|
||||
registry de decoders
|
||||
payload générique JSON/Any
|
||||
execution preparer
|
||||
composition dyn hétérogène
|
||||
identity/version/coverage descriptor
|
||||
payload canonique D3
|
||||
ProgramAccountDecoder / Event / ReturnData
|
||||
ProgramExecutionPreparer
|
||||
execution policy
|
||||
serde / JSON / codec
|
||||
logging / runtime réseau
|
||||
```
|
||||
|
||||
Ces éléments ne doivent pas être simulés côté consumer. Le trait decoder et la preuve d'implémentation externe sont réservés à `pre.004`.
|
||||
Ces surfaces ne doivent pas être simulées côté consumer. Elles attendent les vertical slices qui justifieront leurs contrats réels.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-program-api/src/lib.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![deny(unreachable_pub)]
|
||||
@@ -8,16 +8,19 @@
|
||||
//! Open Program contracts shared by KSP and external Program implementations.
|
||||
//!
|
||||
//! The foundation exposes Core/Interface types plus the minimal instruction
|
||||
//! recognition and decode-outcome vocabulary. Decoder behavior, registries,
|
||||
//! codecs, runtime logging and execution preparation are added only by later
|
||||
//! contracts when their ownership is justified.
|
||||
//! recognition/decode vocabulary and the instruction decoder trait. Registries,
|
||||
//! codecs, runtime logging and execution preparation remain outside this
|
||||
//! foundation until their ownership is justified.
|
||||
|
||||
mod program_instruction_decode;
|
||||
mod program_instruction_decoder;
|
||||
|
||||
/// Result of a successful Program instruction decode attempt.
|
||||
pub use self::program_instruction_decode::ProgramInstructionDecodeOutcome;
|
||||
/// Recognition strength reported by one Program instruction implementation.
|
||||
pub use self::program_instruction_decode::ProgramInstructionRecognition;
|
||||
/// Open contract implemented by one Program instruction decoder.
|
||||
pub use self::program_instruction_decoder::ProgramInstructionDecoder;
|
||||
/// Common KSP error type used by Program-facing contracts.
|
||||
pub use ksp_core_lib::Error;
|
||||
/// Stable structured code identifying a KSP error category and condition.
|
||||
|
||||
28
crates/ksp-program-api/src/program_instruction_decoder.rs
Normal file
28
crates/ksp-program-api/src/program_instruction_decoder.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
// file: crates/ksp-program-api/src/program_instruction_decoder.rs
|
||||
// version: 1
|
||||
|
||||
/// Open contract implemented by one Program instruction decoder.
|
||||
///
|
||||
/// The decoder owns its concrete [`Self::Decoded`] type. No central Program
|
||||
/// enum, erased `Any` payload or serialization contract is required. Program
|
||||
/// identifiers remain opaque [`crate::Pubkey`] values and do not need to be
|
||||
/// registered by Core.
|
||||
///
|
||||
/// Candidate selection is explicit: callers use [`Self::program_ids`] and
|
||||
/// [`Self::recognize`] before invoking [`Self::decode`]. `decode` therefore
|
||||
/// reports only a successful typed value, an intentional unsupported state, or
|
||||
/// a KSP [`crate::Result`] error. The trait defines no default methods and does
|
||||
/// not promise heterogeneous runtime object composition.
|
||||
pub trait ProgramInstructionDecoder: Send + Sync {
|
||||
/// Concrete decoded instruction type owned by the implementation.
|
||||
type Decoded;
|
||||
|
||||
/// Returns the opaque Program identifiers claimed by this decoder.
|
||||
fn program_ids(&self) -> &[crate::Pubkey];
|
||||
|
||||
/// Reports how strongly this decoder recognizes one bounded instruction.
|
||||
fn recognize(&self, instruction: &crate::ProgramInstruction) -> crate::ProgramInstructionRecognition;
|
||||
|
||||
/// Decodes one instruction already selected for this decoder.
|
||||
fn decode(&self, instruction: &crate::ProgramInstruction) -> crate::Result<crate::ProgramInstructionDecodeOutcome<Self::Decoded>>;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-program-api/tests/dependency_boundary.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Dependency and declarative-surface canaries for the Program API foundation.
|
||||
|
||||
@@ -46,10 +46,11 @@ fn pre_002_manifest_has_exact_core_and_interface_runtime_dependencies() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_003_crate_root_adds_only_recognition_and_decode_outcome() {
|
||||
fn pre_004_crate_root_adds_decoder_without_runtime_surface() {
|
||||
let crate_root = include_str!("../src/lib.rs");
|
||||
for required in [
|
||||
"ProgramInstructionDecodeOutcome",
|
||||
"ProgramInstructionDecoder",
|
||||
"ProgramInstructionRecognition",
|
||||
"Error",
|
||||
"ErrorCode",
|
||||
@@ -61,9 +62,18 @@ fn pre_003_crate_root_adds_only_recognition_and_decode_outcome() {
|
||||
] {
|
||||
assert!(crate_root.contains(required), "required Program API facade export missing: {required}");
|
||||
}
|
||||
for forbidden in ["pub mod ", "ProgramInstructionDecoder", "ProgramExecutionPreparer", "TRACING_TARGET", "ksp_logging_lib", "serde", "Any"] {
|
||||
assert!(!crate_root.contains(forbidden), "forbidden pre.003 Program API surface detected: {forbidden}");
|
||||
for forbidden in ["pub mod ", "ProgramExecutionPreparer", "TRACING_TARGET", "ksp_logging_lib", "serde", "Any"] {
|
||||
assert!(!crate_root.contains(forbidden), "forbidden pre.004 Program API surface detected: {forbidden}");
|
||||
}
|
||||
let decoder_source = include_str!("../src/program_instruction_decoder.rs");
|
||||
assert!(decoder_source.contains("pub trait ProgramInstructionDecoder: Send + Sync"));
|
||||
assert!(decoder_source.contains("type Decoded;"));
|
||||
assert!(decoder_source.contains("fn program_ids(&self) -> &[crate::Pubkey];"));
|
||||
assert!(decoder_source.contains("fn recognize(&self, instruction: &crate::ProgramInstruction)"));
|
||||
assert!(decoder_source.contains("crate::Result<crate::ProgramInstructionDecodeOutcome<Self::Decoded>>"));
|
||||
assert!(!decoder_source.contains("ProgramExecutionPreparer"));
|
||||
assert!(!decoder_source.contains("serde"));
|
||||
assert!(!decoder_source.contains("std::any::Any"));
|
||||
let outcome_source = include_str!("../src/program_instruction_decode.rs");
|
||||
for forbidden in ["Ignored", "Failed", "serde", "Any"] {
|
||||
assert!(!outcome_source.contains(forbidden), "forbidden recognition/outcome concept detected: {forbidden}");
|
||||
|
||||
115
crates/ksp-program-api/tests/external_implementation.rs
Normal file
115
crates/ksp-program-api/tests/external_implementation.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
// file: crates/ksp-program-api/tests/external_implementation.rs
|
||||
// version: 1
|
||||
|
||||
//! Downstream-style implementation canary for the open Program decoder contract.
|
||||
|
||||
const EXTERNAL_PROGRAM_ID_BYTES: [u8; 32] = [0xE7_u8; 32];
|
||||
const FOREIGN_PROGRAM_ID_BYTES: [u8; 32] = [0xE8_u8; 32];
|
||||
const SUPPORTED_OPCODE: u8 = 0x2A_u8;
|
||||
|
||||
struct ExternalDecodedInstruction {
|
||||
opcode: u8,
|
||||
}
|
||||
|
||||
struct ExternalProgramDecoder {
|
||||
program_ids: [ksp_program_api::Pubkey; 1],
|
||||
}
|
||||
|
||||
impl ExternalProgramDecoder {
|
||||
fn new() -> Self {
|
||||
return Self { program_ids: [ksp_program_api::Pubkey::new_from_array(EXTERNAL_PROGRAM_ID_BYTES)] };
|
||||
}
|
||||
}
|
||||
|
||||
impl ksp_program_api::ProgramInstructionDecoder for ExternalProgramDecoder {
|
||||
type Decoded = ExternalDecodedInstruction;
|
||||
|
||||
fn program_ids(&self) -> &[ksp_program_api::Pubkey] {
|
||||
return &self.program_ids;
|
||||
}
|
||||
|
||||
fn recognize(&self, instruction: &ksp_program_api::ProgramInstruction) -> ksp_program_api::ProgramInstructionRecognition {
|
||||
if instruction.program_id() != &self.program_ids[0] {
|
||||
return ksp_program_api::ProgramInstructionRecognition::NoMatch;
|
||||
}
|
||||
if instruction.data().first() == std::option::Option::Some(&SUPPORTED_OPCODE) {
|
||||
return ksp_program_api::ProgramInstructionRecognition::ExactMatch;
|
||||
}
|
||||
return ksp_program_api::ProgramInstructionRecognition::ProgramMatch;
|
||||
}
|
||||
|
||||
fn decode(
|
||||
&self,
|
||||
instruction: &ksp_program_api::ProgramInstruction,
|
||||
) -> ksp_program_api::Result<ksp_program_api::ProgramInstructionDecodeOutcome<Self::Decoded>> {
|
||||
if instruction.program_id() != &self.program_ids[0] {
|
||||
return std::result::Result::Ok(ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported);
|
||||
}
|
||||
let opcode = match instruction.data().first() {
|
||||
std::option::Option::Some(value) if *value == SUPPORTED_OPCODE => *value,
|
||||
_ => return std::result::Result::Ok(ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported),
|
||||
};
|
||||
return std::result::Result::Ok(ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(ExternalDecodedInstruction { opcode }));
|
||||
}
|
||||
}
|
||||
|
||||
fn assert_send_sync<T: Send + Sync>(_value: &T) {
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_004_external_decoder_uses_unregistered_pubkey_and_implementation_owned_output() {
|
||||
let decoder = ExternalProgramDecoder::new();
|
||||
assert_send_sync(&decoder);
|
||||
let external_program_id = ksp_program_api::Pubkey::new_from_array(EXTERNAL_PROGRAM_ID_BYTES);
|
||||
assert!(ksp_core_lib::find_program_pubkey(&external_program_id).is_none());
|
||||
assert_eq!(ksp_program_api::ProgramInstructionDecoder::program_ids(&decoder), &[external_program_id]);
|
||||
let exact = ksp_program_api::ProgramInstruction::try_new(external_program_id, std::vec![], std::vec![SUPPORTED_OPCODE]);
|
||||
assert!(exact.is_ok());
|
||||
let exact = match exact {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(ksp_program_api::ProgramInstructionDecoder::recognize(&decoder, &exact), ksp_program_api::ProgramInstructionRecognition::ExactMatch);
|
||||
let outcome = ksp_program_api::ProgramInstructionDecoder::decode(&decoder, &exact);
|
||||
assert!(outcome.is_ok());
|
||||
let outcome = match outcome {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
let decoded = match outcome {
|
||||
ksp_program_api::ProgramInstructionDecodeOutcome::Decoded(value) => value,
|
||||
_ => return,
|
||||
};
|
||||
assert_eq!(decoded.opcode, SUPPORTED_OPCODE);
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pre_004_external_decoder_distinguishes_program_match_unsupported_and_no_match() {
|
||||
let decoder = ExternalProgramDecoder::new();
|
||||
let external_program_id = ksp_program_api::Pubkey::new_from_array(EXTERNAL_PROGRAM_ID_BYTES);
|
||||
let program_only = ksp_program_api::ProgramInstruction::try_new(external_program_id, std::vec![], std::vec![0x11_u8]);
|
||||
assert!(program_only.is_ok());
|
||||
let program_only = match program_only {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(ksp_program_api::ProgramInstructionDecoder::recognize(&decoder, &program_only), ksp_program_api::ProgramInstructionRecognition::ProgramMatch);
|
||||
let unsupported = ksp_program_api::ProgramInstructionDecoder::decode(&decoder, &program_only);
|
||||
assert!(unsupported.is_ok());
|
||||
let unsupported = match unsupported {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert!(matches!(unsupported, ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported));
|
||||
let foreign_program_id = ksp_program_api::Pubkey::new_from_array(FOREIGN_PROGRAM_ID_BYTES);
|
||||
let foreign = ksp_program_api::ProgramInstruction::try_new(foreign_program_id, std::vec![], std::vec![SUPPORTED_OPCODE]);
|
||||
assert!(foreign.is_ok());
|
||||
let foreign = match foreign {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(_) => return,
|
||||
};
|
||||
assert_eq!(ksp_program_api::ProgramInstructionDecoder::recognize(&decoder, &foreign), ksp_program_api::ProgramInstructionRecognition::NoMatch);
|
||||
return;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-program-api/tests/public_api.rs
|
||||
// version: 2
|
||||
// version: 3
|
||||
|
||||
//! Integration canaries for the public `ksp-program-api` foundation.
|
||||
|
||||
@@ -47,3 +47,31 @@ fn public_pre_003_recognition_and_decode_outcome_are_available_from_crate_root()
|
||||
assert_eq!(std::format!("{unsupported:?}"), "Unsupported");
|
||||
return;
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn public_pre_004_instruction_decoder_trait_is_available_from_crate_root() {
|
||||
let decoder = NeverInstantiatedDecoder;
|
||||
assert!(ksp_program_api::ProgramInstructionDecoder::program_ids(&decoder).is_empty());
|
||||
return;
|
||||
}
|
||||
|
||||
struct NeverInstantiatedDecoder;
|
||||
|
||||
impl ksp_program_api::ProgramInstructionDecoder for NeverInstantiatedDecoder {
|
||||
type Decoded = u8;
|
||||
|
||||
fn program_ids(&self) -> &[ksp_program_api::Pubkey] {
|
||||
return &[];
|
||||
}
|
||||
|
||||
fn recognize(&self, _instruction: &ksp_program_api::ProgramInstruction) -> ksp_program_api::ProgramInstructionRecognition {
|
||||
return ksp_program_api::ProgramInstructionRecognition::NoMatch;
|
||||
}
|
||||
|
||||
fn decode(
|
||||
&self,
|
||||
_instruction: &ksp_program_api::ProgramInstruction,
|
||||
) -> ksp_program_api::Result<ksp_program_api::ProgramInstructionDecodeOutcome<Self::Decoded>> {
|
||||
return std::result::Result::Ok(ksp_program_api::ProgramInstructionDecodeOutcome::Unsupported);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user