1use thiserror::Error;
4
5#[derive(Debug, Error)]
7pub enum ProviderError {
8 #[error("Resource not found: {0}")]
10 NotFound(String),
11
12 #[error("Validation error: {0}")]
14 Validation(String),
15
16 #[error("SDK error: {0}")]
18 Sdk(String),
19
20 #[error("Configuration error: {0}")]
22 Configuration(String),
23
24 #[error("Unknown resource type: {0}")]
26 UnknownResource(String),
27
28 #[error("Serialization error: {0}")]
30 Serialization(#[from] serde_json::Error),
31
32 #[error("Transport error: {0}")]
34 Transport(#[from] tonic::transport::Error),
35
36 #[error("Resource already exists: {0}")]
38 AlreadyExists(String),
39
40 #[error("Permission denied: {0}")]
42 PermissionDenied(String),
43
44 #[error("Resource exhausted: {0}")]
46 ResourceExhausted(String),
47
48 #[error("Service unavailable: {0}")]
50 Unavailable(String),
51
52 #[error("Deadline exceeded: {0}")]
54 DeadlineExceeded(String),
55
56 #[error("Failed precondition: {0}")]
58 FailedPrecondition(String),
59
60 #[error("Unimplemented: {0}")]
62 Unimplemented(String),
63
64 #[error("Invalid request: {0}")]
66 InvalidRequest(String),
67}
68
69impl ProviderError {
70 pub fn message(&self) -> &str {
74 match self {
77 Self::NotFound(msg) => msg,
78 Self::Validation(msg) => msg,
79 Self::Sdk(msg) => msg,
80 Self::Configuration(msg) => msg,
81 Self::UnknownResource(msg) => msg,
82 Self::Serialization(_err) => "serialization error (see Debug output)",
83 Self::Transport(_err) => "transport error (see Debug output)",
84 Self::AlreadyExists(msg) => msg,
85 Self::PermissionDenied(msg) => msg,
86 Self::ResourceExhausted(msg) => msg,
87 Self::Unavailable(msg) => msg,
88 Self::DeadlineExceeded(msg) => msg,
89 Self::FailedPrecondition(msg) => msg,
90 Self::Unimplemented(msg) => msg,
91 Self::InvalidRequest(msg) => msg,
92 }
93 }
94
95 #[allow(non_snake_case)]
108 pub fn ConfigurationError(msg: String) -> Self {
109 Self::Configuration(msg)
110 }
111
112 #[allow(non_snake_case)]
123 pub fn SdkError(msg: String) -> Self {
124 Self::Sdk(msg)
125 }
126}
127
128impl From<ProviderError> for tonic::Status {
129 fn from(err: ProviderError) -> Self {
130 match err {
131 ProviderError::NotFound(msg) => tonic::Status::not_found(msg),
132 ProviderError::Validation(msg) => tonic::Status::invalid_argument(msg),
133 ProviderError::Configuration(msg) => tonic::Status::failed_precondition(msg),
134 ProviderError::UnknownResource(msg) => tonic::Status::not_found(msg),
135 ProviderError::Sdk(msg) => tonic::Status::internal(msg),
136 ProviderError::Serialization(err) => {
137 tonic::Status::invalid_argument(format!("Serialization error: {}", err))
138 },
139 ProviderError::Transport(err) => {
140 tonic::Status::unavailable(format!("Transport error: {}", err))
141 },
142 ProviderError::AlreadyExists(msg) => tonic::Status::already_exists(msg),
143 ProviderError::PermissionDenied(msg) => tonic::Status::permission_denied(msg),
144 ProviderError::ResourceExhausted(msg) => tonic::Status::resource_exhausted(msg),
145 ProviderError::Unavailable(msg) => tonic::Status::unavailable(msg),
146 ProviderError::DeadlineExceeded(msg) => tonic::Status::deadline_exceeded(msg),
147 ProviderError::FailedPrecondition(msg) => tonic::Status::failed_precondition(msg),
148 ProviderError::Unimplemented(msg) => tonic::Status::unimplemented(msg),
149 ProviderError::InvalidRequest(msg) => tonic::Status::invalid_argument(msg),
150 }
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn test_error_display() {
160 let err = ProviderError::NotFound("resource-123".to_string());
161 assert_eq!(format!("{}", err), "Resource not found: resource-123");
162
163 let err = ProviderError::Validation("invalid input".to_string());
164 assert_eq!(format!("{}", err), "Validation error: invalid input");
165
166 let err = ProviderError::UnknownResource("custom_resource".to_string());
167 assert_eq!(format!("{}", err), "Unknown resource type: custom_resource");
168 }
169
170 #[test]
171 fn test_error_to_status() {
172 let err = ProviderError::NotFound("test".to_string());
173 let status: tonic::Status = err.into();
174 assert_eq!(status.code(), tonic::Code::NotFound);
175
176 let err = ProviderError::Validation("test".to_string());
177 let status: tonic::Status = err.into();
178 assert_eq!(status.code(), tonic::Code::InvalidArgument);
179
180 let err = ProviderError::Configuration("test".to_string());
181 let status: tonic::Status = err.into();
182 assert_eq!(status.code(), tonic::Code::FailedPrecondition);
183
184 let err = ProviderError::Sdk("test".to_string());
185 let status: tonic::Status = err.into();
186 assert_eq!(status.code(), tonic::Code::Internal);
187 }
188
189 #[test]
190 fn test_new_error_variants_display() {
191 let err = ProviderError::AlreadyExists("bucket-123".to_string());
192 assert_eq!(format!("{}", err), "Resource already exists: bucket-123");
193
194 let err = ProviderError::PermissionDenied("access forbidden".to_string());
195 assert_eq!(format!("{}", err), "Permission denied: access forbidden");
196
197 let err = ProviderError::ResourceExhausted("quota exceeded".to_string());
198 assert_eq!(format!("{}", err), "Resource exhausted: quota exceeded");
199
200 let err = ProviderError::Unavailable("service down".to_string());
201 assert_eq!(format!("{}", err), "Service unavailable: service down");
202
203 let err = ProviderError::DeadlineExceeded("timeout".to_string());
204 assert_eq!(format!("{}", err), "Deadline exceeded: timeout");
205
206 let err = ProviderError::FailedPrecondition("state mismatch".to_string());
207 assert_eq!(format!("{}", err), "Failed precondition: state mismatch");
208
209 let err = ProviderError::Unimplemented("feature not available".to_string());
210 assert_eq!(format!("{}", err), "Unimplemented: feature not available");
211 }
212
213 #[test]
214 fn test_new_error_variants_to_status() {
215 let err = ProviderError::AlreadyExists("test".to_string());
216 let status: tonic::Status = err.into();
217 assert_eq!(status.code(), tonic::Code::AlreadyExists);
218
219 let err = ProviderError::PermissionDenied("test".to_string());
220 let status: tonic::Status = err.into();
221 assert_eq!(status.code(), tonic::Code::PermissionDenied);
222
223 let err = ProviderError::ResourceExhausted("test".to_string());
224 let status: tonic::Status = err.into();
225 assert_eq!(status.code(), tonic::Code::ResourceExhausted);
226
227 let err = ProviderError::Unavailable("test".to_string());
228 let status: tonic::Status = err.into();
229 assert_eq!(status.code(), tonic::Code::Unavailable);
230
231 let err = ProviderError::DeadlineExceeded("test".to_string());
232 let status: tonic::Status = err.into();
233 assert_eq!(status.code(), tonic::Code::DeadlineExceeded);
234
235 let err = ProviderError::FailedPrecondition("test".to_string());
236 let status: tonic::Status = err.into();
237 assert_eq!(status.code(), tonic::Code::FailedPrecondition);
238
239 let err = ProviderError::Unimplemented("test".to_string());
240 let status: tonic::Status = err.into();
241 assert_eq!(status.code(), tonic::Code::Unimplemented);
242 }
243
244 #[test]
245 fn test_invalid_request_variant() {
246 let err = ProviderError::InvalidRequest("bad request".to_string());
247 assert_eq!(format!("{}", err), "Invalid request: bad request");
248
249 let status: tonic::Status = err.into();
250 assert_eq!(status.code(), tonic::Code::InvalidArgument);
251 }
252
253 #[test]
254 fn test_message_method() {
255 let err = ProviderError::NotFound("resource-123".to_string());
256 assert_eq!(err.message(), "resource-123");
257
258 let err = ProviderError::Configuration("invalid config".to_string());
259 assert_eq!(err.message(), "invalid config");
260
261 let err = ProviderError::InvalidRequest("bad request".to_string());
262 assert_eq!(err.message(), "bad request");
263 }
264
265 #[test]
266 fn test_compatibility_aliases() {
267 let err = ProviderError::ConfigurationError("config error".to_string());
269 assert_eq!(err.message(), "config error");
270 assert_eq!(format!("{}", err), "Configuration error: config error");
271
272 let err = ProviderError::SdkError("sdk error".to_string());
274 assert_eq!(err.message(), "sdk error");
275 assert_eq!(format!("{}", err), "SDK error: sdk error");
276 }
277}