#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ShoveError {
#[error("serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("connection error: {0}")]
Connection(String),
#[error("topology error: {0}")]
Topology(String),
#[error("validation error: {0}")]
Validation(String),
#[error("codec error in {codec}: {source}")]
Codec {
codec: &'static str,
#[source]
source: Box<dyn std::error::Error + Send + Sync>,
},
#[error("unknown backend error: {0}")]
Unknown(String),
}
impl ShoveError {
pub fn is_retryable(&self) -> bool {
matches!(self, ShoveError::Connection(_))
}
}
pub type Result<T> = std::result::Result<T, ShoveError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_serialization_error() {
let json_err = serde_json::from_str::<String>("not json").unwrap_err();
let err = ShoveError::Serialization(json_err);
let msg = err.to_string();
assert!(msg.starts_with("serialization error:"), "got: {msg}");
}
#[test]
fn display_connection_error() {
let err = ShoveError::Connection("channel closed".into());
assert_eq!(err.to_string(), "connection error: channel closed");
}
#[test]
fn display_topology_error() {
let err = ShoveError::Topology("missing exchange".into());
assert_eq!(err.to_string(), "topology error: missing exchange");
}
#[test]
fn from_serde_json_error() {
let json_err = serde_json::from_str::<String>("{}").unwrap_err();
let err: ShoveError = json_err.into();
assert!(matches!(err, ShoveError::Serialization(_)));
}
#[test]
fn display_codec_error() {
let inner: Box<dyn std::error::Error + Send + Sync> = "boom".into();
let err = ShoveError::Codec {
codec: "protobuf",
source: inner,
};
let msg = err.to_string();
assert!(msg.contains("codec error in protobuf"), "got: {msg}");
}
#[test]
fn codec_error_is_not_retryable() {
let inner: Box<dyn std::error::Error + Send + Sync> = "boom".into();
let err = ShoveError::Codec {
codec: "json",
source: inner,
};
assert!(!err.is_retryable());
}
}