use thiserror::Error;
use crate::{
common::retry::{
is_fatal_http_error, is_fatal_ws_error, should_retry_http_error, should_retry_ws_error,
},
http::DeriveHttpError,
signing::auth::AuthError,
websocket::DeriveWsError,
};
pub type Result<T> = std::result::Result<T, DeriveError>;
#[derive(Debug, Error)]
pub enum DeriveError {
#[error("HTTP error: {0}")]
Http(#[from] DeriveHttpError),
#[error("WebSocket error: {0}")]
WebSocket(#[from] DeriveWsError),
#[error("auth error: {0}")]
Auth(#[from] AuthError),
#[error("configuration error: {0}")]
Config(String),
}
impl DeriveError {
#[must_use]
pub fn config(msg: impl Into<String>) -> Self {
Self::Config(msg.into())
}
#[must_use]
pub fn is_retryable(&self) -> bool {
match self {
Self::Http(e) => should_retry_http_error(e),
Self::WebSocket(e) => should_retry_ws_error(e),
Self::Auth(_) | Self::Config(_) => false,
}
}
#[must_use]
pub fn is_fatal(&self) -> bool {
match self {
Self::Http(e) => is_fatal_http_error(e),
Self::WebSocket(e) => is_fatal_ws_error(e),
Self::Auth(_) => true,
Self::Config(_) => true,
}
}
}
impl From<serde_json::Error> for DeriveError {
fn from(value: serde_json::Error) -> Self {
Self::Http(DeriveHttpError::Serde(value))
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
fn test_http_transport_is_retryable() {
let err: DeriveError = DeriveHttpError::transport("conn reset").into();
assert!(err.is_retryable());
assert!(!err.is_fatal());
}
#[rstest]
fn test_http_jsonrpc_invalid_params_is_not_retryable() {
let err: DeriveError = DeriveHttpError::JsonRpc {
code: -32602,
message: "Invalid params".to_string(),
data: None,
}
.into();
assert!(!err.is_retryable());
}
#[rstest]
fn test_config_error_is_fatal() {
let err = DeriveError::config("missing constants");
assert!(!err.is_retryable());
assert!(err.is_fatal());
}
#[rstest]
fn test_auth_error_is_fatal() {
let err: DeriveError = AuthError::ClockBeforeEpoch.into();
assert!(!err.is_retryable());
assert!(err.is_fatal());
}
#[rstest]
fn test_ws_transport_is_retryable() {
let err: DeriveError = DeriveWsError::transport("broken pipe").into();
assert!(err.is_retryable());
}
}