1use std::{
33 net::AddrParseError,
34 sync::{MutexGuard, PoisonError},
35};
36
37use http::uri::InvalidUri;
38use thiserror::Error;
39
40#[derive(Debug, Error)]
45pub enum VineError {
46 #[error("SideCar '{0}' not found or its gRPC client channel is not ready.")]
49 ClientNotConnected(String),
50
51 #[error("Failed to connect to sidecar '{SideCarIdentifier}' at '{Address}': {Reason}")]
53 ConnectionFailed { SideCarIdentifier:String, Address:String, Reason:String },
54
55 #[error("Connection to sidecar '{0}' was lost")]
57 ConnectionLost(String),
58
59 #[error("gRPC call failed: {0}")]
61 RPCError(String),
62
63 #[error(
65 "Request to sidecar '{SideCarIdentifier}' (method: '{MethodName}') timed out after {TimeoutMilliseconds}ms"
66 )]
67 RequestTimeout { SideCarIdentifier:String, MethodName:String, TimeoutMilliseconds:u64 },
68
69 #[error("Request to sidecar '{SideCarIdentifier}' (method: '{MethodName}') was canceled")]
71 RequestCanceled { SideCarIdentifier:String, MethodName:String },
72
73 #[error("JSON serialization error for gRPC payload: {0}")]
75 SerializationError(#[from] serde_json::Error),
76
77 #[error("Message size {ActualSize} bytes exceeds maximum allowed size {MaxSize} bytes")]
79 MessageTooLarge { ActualSize:usize, MaxSize:usize },
80
81 #[error("Invalid message format: {0}")]
83 InvalidMessageFormat(String),
84
85 #[error("Tonic transport error: {0}")]
87 TonicTransportError(#[from] tonic::transport::Error),
88
89 #[error("Internal state lock poisoned: {0}")]
92 InternalLockError(String),
93
94 #[error("Invalid internal state detected: {0}")]
97 InvalidState(String),
98
99 #[error("Invalid URI: {0}")]
101 InvalidUri(#[from] InvalidUri),
102
103 #[error("Invalid Socket Address: {0}")]
105 AddressParseError(#[from] AddrParseError),
106}
107
108impl VineError {
109 pub fn IsRecoverable(&self) -> bool {
112 matches!(
113 self,
114 Self::RequestTimeout { .. }
115 | Self::ConnectionFailed { .. }
116 | Self::ConnectionLost(_)
117 | Self::TonicTransportError(_)
118 )
119 }
120
121 pub fn ToTonicStatus(&self) -> tonic::Status {
123 match self {
124 Self::RequestTimeout { .. } => tonic::Status::deadline_exceeded(self.to_string()),
125
126 Self::ClientNotConnected(_) | Self::ConnectionFailed { .. } => tonic::Status::unavailable(self.to_string()),
127
128 Self::SerializationError(_) | Self::InternalLockError(_) | Self::InvalidState(_) => {
129 tonic::Status::internal(self.to_string())
130 },
131
132 Self::MessageTooLarge { .. } => tonic::Status::resource_exhausted(self.to_string()),
133
134 Self::InvalidMessageFormat(_) | Self::InvalidUri(_) | Self::AddressParseError(_) => {
135 tonic::Status::invalid_argument(self.to_string())
136 },
137
138 Self::RequestCanceled { .. } => tonic::Status::cancelled(self.to_string()),
139
140 Self::RPCError(msg) => tonic::Status::unknown(msg.clone()),
141
142 Self::ConnectionLost(_) => tonic::Status::aborted(self.to_string()),
143
144 Self::TonicTransportError(_) => tonic::Status::unavailable(self.to_string()),
145 }
146 }
147}
148
149impl<T> From<PoisonError<MutexGuard<'_, T>>> for VineError {
150 fn from(Error:PoisonError<MutexGuard<'_, T>>) -> Self {
151 VineError::InternalLockError(format!("Shared state lock poisoned: {}", Error))
152 }
153}
154
155impl From<tonic::Status> for VineError {
156 fn from(Status:tonic::Status) -> Self {
157 match Status.code() {
158 tonic::Code::DeadlineExceeded => VineError::RPCError(format!("Timeout: {}", Status.message())),
159
160 tonic::Code::NotFound => VineError::ClientNotConnected(Status.message().to_string()),
161
162 tonic::Code::AlreadyExists | tonic::Code::InvalidArgument | tonic::Code::OutOfRange => {
163 VineError::InvalidMessageFormat(Status.message().to_string())
164 },
165
166 tonic::Code::FailedPrecondition | tonic::Code::Aborted => {
167 VineError::ConnectionLost(Status.message().to_string())
168 },
169
170 tonic::Code::ResourceExhausted => VineError::MessageTooLarge { ActualSize:0, MaxSize:4 * 1024 * 1024 },
171
172 tonic::Code::Cancelled => {
173 VineError::RequestCanceled { SideCarIdentifier:"unknown".to_string(), MethodName:"unknown".to_string() }
174 },
175
176 tonic::Code::Unavailable => {
177 VineError::ConnectionFailed {
178 SideCarIdentifier:"unknown".to_string(),
179
180 Address:"unknown".to_string(),
181
182 Reason:Status.message().to_string(),
183 }
184 },
185
186 _ => VineError::RPCError(Status.to_string()),
187 }
188 }
189}
190
191pub type Result<T> = std::result::Result<T, VineError>;