use thiserror::Error;
#[non_exhaustive]
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid HRP: {0}")]
InvalidHrp(String),
#[error("mixed case in input string")]
MixedCase,
#[error("invalid data-part length: {0}")]
InvalidStringLength(usize),
#[error("invalid character {ch} at position {position}")]
InvalidChar {
ch: char,
position: usize,
},
#[error("BCH uncorrectable: {0}")]
BchUncorrectable(String),
#[error("unsupported card type: 0x{0:02x}")]
UnsupportedCardType(u8),
#[error("malformed payload padding (5-bit symbols don't byte-align)")]
MalformedPayloadPadding,
#[error("chunk_set_id mismatch across chunks")]
ChunkSetIdMismatch,
#[error("chunked-header malformed: {0}")]
ChunkedHeaderMalformed(String),
#[error("mixed string-layer header types in input list")]
MixedHeaderTypes,
#[error("cross-chunk integrity hash mismatch")]
CrossChunkHashMismatch,
#[error("unsupported version: {0}")]
UnsupportedVersion(u8),
#[error("reserved bits set in bytecode header")]
ReservedBitsSet,
#[error("policy_id_stub_count must be >= 1")]
InvalidPolicyIdStubCount,
#[error("invalid path indicator byte: 0x{0:02x}")]
InvalidPathIndicator(u8),
#[error("path too deep: {0} components (max 10)")]
PathTooDeep(u8),
#[error("invalid path component: {0}")]
InvalidPathComponent(String),
#[error("invalid xpub version: 0x{0:08x}")]
InvalidXpubVersion(u32),
#[error("invalid xpub public key: {0}")]
InvalidXpubPublicKey(String),
#[error("unexpected end of bytecode")]
UnexpectedEnd,
#[error("trailing bytes after xpub")]
TrailingBytes,
#[error(
"card payload too large: bytecode_len = {bytecode_len} > max_supported = {max_supported}"
)]
CardPayloadTooLarge {
bytecode_len: usize,
max_supported: usize,
},
}
pub type Result<T> = core::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parameterized_variants_render() {
let cases: Vec<(Error, &str)> = vec![
(Error::InvalidHrp("mq".into()), "invalid HRP: mq"),
(
Error::BchUncorrectable(
"5 substitutions exceed long-code 4-correction limit".into(),
),
"BCH uncorrectable: 5 substitutions exceed long-code 4-correction limit",
),
(
Error::UnsupportedCardType(0x05),
"unsupported card type: 0x05",
),
(
Error::ChunkedHeaderMalformed("total_chunks = 0".into()),
"chunked-header malformed: total_chunks = 0",
),
(
Error::InvalidXpubPublicKey("malformed compressed point".into()),
"invalid xpub public key: malformed compressed point",
),
(Error::UnsupportedVersion(1), "unsupported version: 1"),
(
Error::InvalidPathIndicator(0x16),
"invalid path indicator byte: 0x16",
),
(
Error::PathTooDeep(11),
"path too deep: 11 components (max 10)",
),
(
Error::InvalidPathComponent("LEB128 overflow at component 3".into()),
"invalid path component: LEB128 overflow at component 3",
),
(
Error::InvalidXpubVersion(0xDEADBEEF),
"invalid xpub version: 0xdeadbeef",
),
];
for (err, expected) in cases {
assert_eq!(format!("{err}"), expected);
}
}
#[test]
fn static_variants_render() {
assert_eq!(
format!("{}", Error::ReservedBitsSet),
"reserved bits set in bytecode header",
);
assert_eq!(
format!("{}", Error::CrossChunkHashMismatch),
"cross-chunk integrity hash mismatch",
);
assert_eq!(
format!("{}", Error::ChunkSetIdMismatch),
"chunk_set_id mismatch across chunks",
);
assert_eq!(
format!("{}", Error::MixedHeaderTypes),
"mixed string-layer header types in input list",
);
assert_eq!(
format!("{}", Error::MalformedPayloadPadding),
"malformed payload padding (5-bit symbols don't byte-align)",
);
assert_eq!(
format!("{}", Error::InvalidPolicyIdStubCount),
"policy_id_stub_count must be >= 1",
);
assert_eq!(
format!("{}", Error::UnexpectedEnd),
"unexpected end of bytecode",
);
assert_eq!(
format!("{}", Error::TrailingBytes),
"trailing bytes after xpub",
);
}
}