use serde_json::json;
#[non_exhaustive]
#[derive(Debug)]
pub enum CliError {
Codec(mk_codec::Error),
MdCodec(md_codec::Error),
#[allow(dead_code)]
FutureFormat(String),
ContentMismatch {
field: String,
expected: String,
actual: String,
},
UsageError(String),
IoError(std::io::Error),
}
impl CliError {
pub fn kind(&self) -> &'static str {
match self {
CliError::Codec(e) => mk_codec_error_kind(e),
CliError::MdCodec(_) => "MdCodec",
CliError::FutureFormat(_) => "FutureFormat",
CliError::ContentMismatch { .. } => "ContentMismatch",
CliError::UsageError(_) => "UsageError",
CliError::IoError(_) => "IoError",
}
}
pub fn message(&self) -> String {
match self {
CliError::Codec(e) => format!("{e}"),
CliError::MdCodec(e) => format!("md1 input rejected: {e}"),
CliError::FutureFormat(m) => m.clone(),
CliError::ContentMismatch {
field,
expected,
actual,
} => format!("verify mismatch on {field}: expected {expected}, got {actual}"),
CliError::UsageError(m) => m.clone(),
CliError::IoError(e) => format!("io error: {e}"),
}
}
pub fn exit_code(&self) -> u8 {
match self {
CliError::Codec(mk_codec::Error::UnsupportedVersion(_)) => 3,
CliError::Codec(_) | CliError::MdCodec(_) => 2,
CliError::FutureFormat(_) => 3,
CliError::ContentMismatch { .. } => 4,
CliError::UsageError(_) => 64,
CliError::IoError(_) => 1,
}
}
pub fn details(&self) -> Option<serde_json::Value> {
match self {
CliError::ContentMismatch {
field,
expected,
actual,
} => Some(json!({
"field": field,
"expected": expected,
"actual": actual,
})),
CliError::FutureFormat(m) => Some(json!({ "message": m })),
_ => None,
}
}
}
fn mk_codec_error_kind(e: &mk_codec::Error) -> &'static str {
match e {
mk_codec::Error::InvalidHrp(_) => "InvalidHrp",
mk_codec::Error::MixedCase => "MixedCase",
mk_codec::Error::InvalidStringLength(_) => "InvalidStringLength",
mk_codec::Error::InvalidChar { .. } => "InvalidChar",
mk_codec::Error::BchUncorrectable(_) => "BchUncorrectable",
mk_codec::Error::UnsupportedCardType(_) => "UnsupportedCardType",
mk_codec::Error::MalformedPayloadPadding => "MalformedPayloadPadding",
mk_codec::Error::ChunkSetIdMismatch => "ChunkSetIdMismatch",
mk_codec::Error::ChunkedHeaderMalformed(_) => "ChunkedHeaderMalformed",
mk_codec::Error::MixedHeaderTypes => "MixedHeaderTypes",
mk_codec::Error::CrossChunkHashMismatch => "CrossChunkHashMismatch",
mk_codec::Error::UnsupportedVersion(_) => "UnsupportedVersion",
mk_codec::Error::ReservedBitsSet => "ReservedBitsSet",
mk_codec::Error::InvalidPolicyIdStubCount => "InvalidPolicyIdStubCount",
mk_codec::Error::InvalidPathIndicator(_) => "InvalidPathIndicator",
mk_codec::Error::PathTooDeep(_) => "PathTooDeep",
mk_codec::Error::InvalidPathComponent(_) => "InvalidPathComponent",
mk_codec::Error::InvalidXpubVersion(_) => "InvalidXpubVersion",
mk_codec::Error::InvalidXpubPublicKey(_) => "InvalidXpubPublicKey",
mk_codec::Error::UnexpectedEnd => "UnexpectedEnd",
mk_codec::Error::TrailingBytes => "TrailingBytes",
mk_codec::Error::CardPayloadTooLarge { .. } => "CardPayloadTooLarge",
_ => "Unknown",
}
}
impl std::fmt::Display for CliError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "error: {}", self.message())
}
}
impl std::error::Error for CliError {}
impl From<mk_codec::Error> for CliError {
fn from(e: mk_codec::Error) -> Self {
CliError::Codec(e)
}
}
impl From<md_codec::Error> for CliError {
fn from(e: md_codec::Error) -> Self {
CliError::MdCodec(e)
}
}
impl From<std::io::Error> for CliError {
fn from(e: std::io::Error) -> Self {
CliError::IoError(e)
}
}
pub type Result<T> = core::result::Result<T, CliError>;