use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[non_exhaustive]
#[derive(Debug, Error, Clone, PartialEq, Eq)]
pub enum Error {
#[error("Transport not available: {0}")]
TransportNotAvailable(String),
#[error("Peer not reachable: {0}")]
PeerNotReachable(String),
#[error("Send failed: {0}")]
SendFailed(String),
#[error("Receive failed: {0}")]
ReceiveFailed(String),
#[error("Configuration error: {0}")]
ConfigurationError(String),
#[error("Serialization error: {0}")]
SerializationError(String),
#[error("Core error: {0}")]
Core(#[from] offline_protocol_core::Error),
#[error("Message too large: {0} bytes exceeds limit of {1} bytes")]
MessageTooLarge(usize, usize),
#[error("Crypto error: {0}")]
CryptoError(String),
#[error("{0}")]
Other(String),
}
impl Error {
pub fn code(&self) -> &'static str {
match self {
Self::TransportNotAvailable(_) => "TRANSPORT_NOT_AVAILABLE",
Self::PeerNotReachable(_) => "PEER_NOT_REACHABLE",
Self::SendFailed(_) => "SEND_FAILED",
Self::ReceiveFailed(_) => "RECEIVE_FAILED",
Self::ConfigurationError(_) => "CONFIGURATION_ERROR",
Self::SerializationError(_) => "SERIALIZATION_ERROR",
Self::Core(_) => "CORE_ERROR",
Self::MessageTooLarge(_, _) => "MESSAGE_TOO_LARGE",
Self::CryptoError(_) => "CRYPTO_ERROR",
Self::Other(_) => "OTHER",
}
}
}
#[cfg(test)]
mod tests {
use super::Error;
#[test]
fn transport_error_code_values_are_stable() {
assert_eq!(
Error::TransportNotAvailable("ble".into()).code(),
"TRANSPORT_NOT_AVAILABLE"
);
assert_eq!(
Error::PeerNotReachable("peer".into()).code(),
"PEER_NOT_REACHABLE"
);
assert_eq!(Error::SendFailed("boom".into()).code(), "SEND_FAILED");
assert_eq!(Error::ReceiveFailed("boom".into()).code(), "RECEIVE_FAILED");
assert_eq!(
Error::ConfigurationError("bad".into()).code(),
"CONFIGURATION_ERROR"
);
assert_eq!(
Error::SerializationError("bad".into()).code(),
"SERIALIZATION_ERROR"
);
assert_eq!(
Error::Core(offline_protocol_core::Error::Other("x".into())).code(),
"CORE_ERROR"
);
assert_eq!(
Error::MessageTooLarge(2048, 1024).code(),
"MESSAGE_TOO_LARGE"
);
assert_eq!(Error::CryptoError("kdf".into()).code(), "CRYPTO_ERROR");
assert_eq!(Error::Other("opaque".into()).code(), "OTHER");
}
#[test]
fn transport_error_code_is_independent_of_display() {
let err = Error::SendFailed("transient radio failure".into());
assert_eq!(err.code(), "SEND_FAILED");
assert!(err.to_string().contains("transient radio failure"));
}
}