#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error(transparent)]
Connect(#[from] tonic::transport::Error),
#[error(transparent)]
Rpc(#[from] tonic::Status),
#[error(
"gateway does not support {operation}; upgrade the gateway and client to compatible protocol versions"
)]
IncompatibleGateway { operation: &'static str },
#[error("protocol error: {0}")]
Protocol(String),
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_rpc_display_is_transparent() {
let status = tonic::Status::internal("boom");
let expected = status.to_string();
let err: Error = status.into();
assert_eq!(err.to_string(), expected);
}
#[test]
fn test_error_rpc_matches_variant() {
let status = tonic::Status::not_found("missing");
let err: Error = status.into();
assert!(matches!(err, Error::Rpc(_)));
}
#[test]
fn test_error_rpc_debug_matches_variant_and_status() {
let status = tonic::Status::internal("boom");
let err: Error = status.clone().into();
assert_eq!(format!("{err:?}"), format!("Rpc({status:?})"));
}
#[test]
fn test_error_rpc_anyhow_debug_matches_bare_status() {
let status = tonic::Status::internal("boom");
let bare = anyhow::Error::from(status.clone());
let wrapped = anyhow::Error::from(Error::from(status));
assert_eq!(format!("{bare:?}"), format!("{wrapped:?}"));
assert_eq!(bare.to_string(), wrapped.to_string());
}
#[test]
fn test_protocol_error_is_actionable() {
let err = Error::Protocol("unknown browse node kind".into());
assert_eq!(err.to_string(), "protocol error: unknown browse node kind");
}
#[test]
fn test_incompatible_gateway_error_is_actionable() {
let err = Error::IncompatibleGateway {
operation: "paged browse",
};
assert!(err.to_string().contains("upgrade the gateway and client"));
}
}