use thiserror::Error;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, Error)]
pub enum Error {
#[error("HTTP error: {0}")]
Http(#[from] reqwest::Error),
#[error("Rate limit exceeded: please retry after {retry_after} seconds")]
RateLimitExceeded {
retry_after: u64,
},
#[error("Unauthorized: invalid API key")]
Unauthorized,
#[error("API error (status {status}): {message}")]
ApiError {
status: u16,
message: String,
},
#[error("Deserialization error: {0}")]
Deserialization(#[from] serde_json::Error),
#[error("Invalid parameter: {0}")]
InvalidParameter(String),
#[error("Invalid request: {0}")]
InvalidRequest(String),
#[cfg(feature = "websocket")]
#[error("WebSocket error: {0}")]
WebSocket(#[from] tokio_tungstenite::tungstenite::Error),
#[error("URL parse error: {0}")]
UrlParse(#[from] url::ParseError),
#[error("Request timeout")]
Timeout,
#[error("Internal error: {0}")]
Internal(String),
}
impl Error {
pub fn invalid_parameter(param: impl Into<String>) -> Self {
Self::InvalidParameter(param.into())
}
pub fn internal(message: impl Into<String>) -> Self {
Self::Internal(message.into())
}
pub fn is_retryable(&self) -> bool {
matches!(
self,
Self::RateLimitExceeded { .. } | Self::Timeout | Self::Http(_)
)
}
pub fn retry_after(&self) -> Option<u64> {
match self {
Self::RateLimitExceeded { retry_after } => Some(*retry_after),
Self::Timeout => Some(5), _ => None,
}
}
}