open-standards/openpay/crates/op-iso8583/src/error.rs
Transaction Science 01d4ad9c7b Ingest batch 2: 9 new crates across openpay / smart-byte / eoc
openpay/ (+3 crates, ~8400 LOC, 162 tests)
  op-iso8583/         ISO 8583 messages for direct acquirer / card-network
                      connectivity. MTIs, primary+secondary bitmaps, full
                      DE catalogue, BCD/ASCII/EBCDIC/LLVAR/LLLVAR codecs,
                      five dialects (Visa Base I, Mastercard MDS, Amex GNS,
                      Discover, JCB), network-management messages
                      (sign-on/echo/cutover), DE 55 EMV TLV.
  op-mobile-wallets/  Apple Pay (ECDH-P256 + AES-256-GCM), Google Pay ECv2
                      (ECDH + HKDF + AES-256-CTR + HMAC), Samsung Pay. PAN
                      never leaves the decryption boundary as a String;
                      lands in VaultRef. No-PAN-leakage assertions per
                      wallet.
  op-routing/         Least-cost routing + MCC-aware routing + intelligent
                      retry / soft-decline recovery. 231-MCC catalogue.
                      Real ISO 8583 DE-39 soft/hard decline taxonomies.

smart-byte/smart-byte-rs/ (+3 crates, ~7100 LOC, 96 tests)
  smart-byte-vc/      W3C Verifiable Credentials 2.0: VCDM, presentations,
                      Data Integrity proofs (eddsa-jcs-2022), VC-JWT,
                      SD-JWT (selective disclosure), Bitstring Status
                      List 2021. Cargo-bridge maps VCs to Smart Byte
                      envelopes with SAID stability.
  smart-byte-did/     W3C DID resolution: did:key (Ed25519/P-256/secp256k1),
                      did:web (HTTPS), did:peer (numalgo 2+4), did:jwk,
                      did:ion (feature-gated stub). DID URL dereferencer.
  smart-byte-pq/      Post-quantum signatures: ML-DSA-44/65/87 (FIPS 204),
                      SLH-DSA across 12 parameter sets (FIPS 205), FN-DSA
                      (draft FIPS 206, feature-gated). Hybrid Ed25519 +
                      ML-DSA pattern for the transition. Secret-key
                      zeroize-on-drop.

eoc/eoc-rs/ (+3 crates, ~6450 LOC, 76 tests)
  eoc-vendor-api/     NeuralBackend implementations: Anthropic, OpenAI,
                      Google, Mistral, Cohere, Groq, Together, Fireworks.
                      SSE streaming. Prompt-caching where supported.
                      Joule estimates per published energy coefficient
                      (12-model curated table).
  eoc-embeddings/     21 embedding models behind one Embedder trait:
                      OpenAI v3 (3), Cohere v3 (3), Voyage (5), Mistral,
                      Jina, plus local ONNX (BGE x3, nomic, mxbai, GTE,
                      E5) behind 'local' feature. Matryoshka-style
                      dimension projection. Per-content-hash cache.
  eoc-route-learned/  RouteLLM-style matrix-factorisation router +
                      multinomial-logreg classifier + LinUCB / Thompson
                      bandits for online learning. LearnedCascade wraps
                      the rule-based cascade with a confidence-threshold
                      skip policy. Synthetic-eval accuracy: MF 0.935,
                      LogReg 0.980.

Quality:
- cargo check across all three workspaces: clean
- cargo clippy --all-targets -- -D warnings on every new crate: clean
- 334 new tests total; all passing
- #![forbid(unsafe_code)] across every new crate
- No unwrap/expect outside #[cfg(test)]

Rust 1.95.0 edition 2024 (inherited via root rust-toolchain.toml).
2026-05-23 18:12:24 -04:00

106 lines
3.5 KiB
Rust

//! Typed errors for `op-iso8583`. One sealed enum per project convention.
use thiserror::Error;
/// Result alias for `op-iso8583`.
pub type Result<T> = core::result::Result<T, Error>;
/// Failure modes for ISO 8583 message handling.
#[derive(Debug, Error)]
pub enum Error {
/// The byte slice ran out mid-decode.
#[error("unexpected end of input at offset {offset} (needed {needed} bytes)")]
UnexpectedEof {
/// Offset into the input where the read began.
offset: usize,
/// Number of bytes the codec needed.
needed: usize,
},
/// A byte was not a valid BCD nibble (0x0..=0x9) when one was expected.
#[error("invalid BCD nibble 0x{nibble:02x} at offset {offset}")]
InvalidBcd {
/// Offending nibble (high or low).
nibble: u8,
/// Offset into the input.
offset: usize,
},
/// A byte was not valid ASCII (or fell outside the 0x20..=0x7E printable
/// range where the dialect required it).
#[error("invalid ASCII byte 0x{byte:02x} at offset {offset}")]
InvalidAscii {
/// Offending byte.
byte: u8,
/// Offset into the input.
offset: usize,
},
/// A variable-length field carried a length prefix outside the allowed
/// range for its encoding (LL: 0..=99, LLL: 0..=999).
#[error("invalid variable-length prefix {prefix} (max {max})")]
InvalidVarLength {
/// Decoded length value.
prefix: usize,
/// Maximum legal length for the encoding.
max: usize,
},
/// The Message Type Indicator was not four BCD/ASCII numeric digits.
#[error("invalid MTI: {0}")]
InvalidMti(String),
/// A bitmap bit was set referencing a data element with no decoder
/// registered in this dialect.
#[error("unknown data element {0}")]
UnknownDataElement(u8),
/// A required data element was missing from the bitmap.
#[error("missing required data element {0}")]
MissingDataElement(u8),
/// A data element's decoded value did not satisfy its semantic
/// constraints (e.g. DE 39 response code not 2 digits, DE 49
/// currency not 3 numeric digits).
#[error("invalid data element {de}: {reason}")]
InvalidDataElement {
/// Data-element number.
de: u8,
/// Human-readable reason.
reason: String,
},
/// Track 2 / DE 35 was malformed (missing separator, bad PAN, bad
/// expiration date, etc.).
#[error("invalid track 2 data: {0}")]
InvalidTrack2(&'static str),
/// DE 55 EMV TLV could not be walked. Carries the failing offset
/// (within the DE 55 value) and a static reason.
#[error("invalid EMV TLV at offset {offset}: {reason}")]
InvalidEmvTlv {
/// Offset into the DE 55 value.
offset: usize,
/// Static reason ("length overflows value", "constructed before primitive", ...).
reason: &'static str,
},
/// MAC verification failed: computed MAC did not match the value
/// carried in DE 64 / DE 128.
#[error("MAC mismatch")]
MacMismatch,
/// The dialect rejected a field for a network-specific reason
/// (e.g. Mastercard MDS requires DE 22 sub-fields the message lacks).
#[error("dialect {dialect} rejected: {reason}")]
DialectViolation {
/// Which dialect.
dialect: &'static str,
/// Why.
reason: String,
},
/// Forwarded `op-core` error (currency / overflow during Money mapping).
#[error(transparent)]
Core(#[from] op_core::Error),
}