1use thiserror::Error;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13#[repr(i32)]
14pub enum ErrorCode {
15 InvalidArgument = -1,
17 InvalidState = -2,
19 Timeout = -3,
21 Cancelled = -4,
23 TransportError = -5,
25 NegotiationFailed = -6,
27 ExtensionUnavailable = -7,
29 PolicyDenied = -8,
31 InternalError = -9,
33 ResourceExhausted = -10,
35}
36
37impl std::fmt::Display for ErrorCode {
38 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39 match self {
40 Self::InvalidArgument => write!(f, "INVALID_ARGUMENT"),
41 Self::InvalidState => write!(f, "INVALID_STATE"),
42 Self::Timeout => write!(f, "TIMEOUT"),
43 Self::Cancelled => write!(f, "CANCELLED"),
44 Self::TransportError => write!(f, "TRANSPORT_ERROR"),
45 Self::NegotiationFailed => write!(f, "NEGOTIATION_FAILED"),
46 Self::ExtensionUnavailable => write!(f, "EXTENSION_UNAVAILABLE"),
47 Self::PolicyDenied => write!(f, "POLICY_DENIED"),
48 Self::InternalError => write!(f, "INTERNAL_ERROR"),
49 Self::ResourceExhausted => write!(f, "RESOURCE_EXHAUSTED"),
50 }
51 }
52}
53
54#[derive(Error, Debug)]
56pub enum Error {
57 #[error("Invalid argument: {message}")]
58 InvalidArgument { message: String },
59
60 #[error("Invalid state: expected {expected}, got {actual}")]
61 InvalidState { expected: String, actual: String },
62
63 #[error("Request timed out after {timeout_ms}ms")]
64 Timeout { timeout_ms: u64 },
65
66 #[error("Request cancelled")]
67 Cancelled,
68
69 #[error("Transport error: {message}")]
70 TransportError { message: String },
71
72 #[error("Negotiation failed: {message}")]
73 NegotiationFailed { message: String },
74
75 #[error("Extension unavailable: {extension}")]
76 ExtensionUnavailable { extension: String },
77
78 #[error("Policy denied: {message}")]
79 PolicyDenied { message: String },
80
81 #[error("Internal error: {message}")]
82 InternalError { message: String },
83
84 #[error("Resource exhausted: {resource}")]
85 ResourceExhausted { resource: String },
86
87 #[error("JSON error: {0}")]
88 Json(#[from] serde_json::Error),
89
90 #[error("IO error: {0}")]
91 Io(#[from] std::io::Error),
92}
93
94impl Error {
95 pub fn code(&self) -> ErrorCode {
97 match self {
98 Self::InvalidArgument { .. } => ErrorCode::InvalidArgument,
99 Self::InvalidState { .. } => ErrorCode::InvalidState,
100 Self::Timeout { .. } => ErrorCode::Timeout,
101 Self::Cancelled => ErrorCode::Cancelled,
102 Self::TransportError { .. } => ErrorCode::TransportError,
103 Self::NegotiationFailed { .. } => ErrorCode::NegotiationFailed,
104 Self::ExtensionUnavailable { .. } => ErrorCode::ExtensionUnavailable,
105 Self::PolicyDenied { .. } => ErrorCode::PolicyDenied,
106 Self::InternalError { .. } => ErrorCode::InternalError,
107 Self::ResourceExhausted { .. } => ErrorCode::ResourceExhausted,
108 Self::Json(_) => ErrorCode::InvalidArgument,
109 Self::Io(_) => ErrorCode::TransportError,
110 }
111 }
112
113 pub fn is_retryable(&self) -> bool {
115 matches!(
116 self.code(),
117 ErrorCode::Timeout | ErrorCode::TransportError | ErrorCode::ResourceExhausted
118 )
119 }
120}
121
122pub type Result<T> = std::result::Result<T, Error>;