use thiserror::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(i32)]
pub enum ErrorCode {
InvalidArgument = -1,
InvalidState = -2,
Timeout = -3,
Cancelled = -4,
TransportError = -5,
NegotiationFailed = -6,
ExtensionUnavailable = -7,
PolicyDenied = -8,
InternalError = -9,
ResourceExhausted = -10,
}
impl std::fmt::Display for ErrorCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::InvalidArgument => write!(f, "INVALID_ARGUMENT"),
Self::InvalidState => write!(f, "INVALID_STATE"),
Self::Timeout => write!(f, "TIMEOUT"),
Self::Cancelled => write!(f, "CANCELLED"),
Self::TransportError => write!(f, "TRANSPORT_ERROR"),
Self::NegotiationFailed => write!(f, "NEGOTIATION_FAILED"),
Self::ExtensionUnavailable => write!(f, "EXTENSION_UNAVAILABLE"),
Self::PolicyDenied => write!(f, "POLICY_DENIED"),
Self::InternalError => write!(f, "INTERNAL_ERROR"),
Self::ResourceExhausted => write!(f, "RESOURCE_EXHAUSTED"),
}
}
}
#[derive(Error, Debug)]
pub enum Error {
#[error("Invalid argument: {message}")]
InvalidArgument { message: String },
#[error("Invalid state: expected {expected}, got {actual}")]
InvalidState { expected: String, actual: String },
#[error("Request timed out after {timeout_ms}ms")]
Timeout { timeout_ms: u64 },
#[error("Request cancelled")]
Cancelled,
#[error("Transport error: {message}")]
TransportError { message: String },
#[error("Negotiation failed: {message}")]
NegotiationFailed { message: String },
#[error("Extension unavailable: {extension}")]
ExtensionUnavailable { extension: String },
#[error("Policy denied: {message}")]
PolicyDenied { message: String },
#[error("Internal error: {message}")]
InternalError { message: String },
#[error("Resource exhausted: {resource}")]
ResourceExhausted { resource: String },
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
impl Error {
pub fn code(&self) -> ErrorCode {
match self {
Self::InvalidArgument { .. } => ErrorCode::InvalidArgument,
Self::InvalidState { .. } => ErrorCode::InvalidState,
Self::Timeout { .. } => ErrorCode::Timeout,
Self::Cancelled => ErrorCode::Cancelled,
Self::TransportError { .. } => ErrorCode::TransportError,
Self::NegotiationFailed { .. } => ErrorCode::NegotiationFailed,
Self::ExtensionUnavailable { .. } => ErrorCode::ExtensionUnavailable,
Self::PolicyDenied { .. } => ErrorCode::PolicyDenied,
Self::InternalError { .. } => ErrorCode::InternalError,
Self::ResourceExhausted { .. } => ErrorCode::ResourceExhausted,
Self::Json(_) => ErrorCode::InvalidArgument,
Self::Io(_) => ErrorCode::TransportError,
}
}
pub fn is_retryable(&self) -> bool {
matches!(
self.code(),
ErrorCode::Timeout | ErrorCode::TransportError | ErrorCode::ResourceExhausted
)
}
}
pub type Result<T> = std::result::Result<T, Error>;