1use thiserror::Error;
6
7#[derive(Error, Debug)]
9pub enum CloudError {
10 #[error("Failed to initialize cloud client: {0}")]
12 ClientInit(String),
13
14 #[error("Cloud provider '{0}' not enabled. Enable with feature flag (cloud-aws, cloud-gcp, cloud-azure)")]
15 ProviderNotEnabled(String),
16
17 #[error("Failed to fetch secret '{name}': {error}")]
19 SecretFetch {
20 name: String,
21 error: String,
22 },
23
24 #[error("Failed to create secret '{name}': {error}")]
25 SecretCreate {
26 name: String,
27 error: String,
28 },
29
30 #[error("Failed to update secret '{name}': {error}")]
31 SecretUpdate {
32 name: String,
33 error: String,
34 },
35
36 #[error("Failed to delete secret '{name}': {error}")]
37 SecretDelete {
38 name: String,
39 error: String,
40 },
41
42 #[error("Invalid secret format for '{name}': {reason}")]
43 SecretFormat {
44 name: String,
45 reason: String,
46 },
47
48 #[error("Failed to list secrets: {0}")]
49 SecretList(String),
50
51 #[error("Secret not found: '{0}'")]
52 SecretNotFound(String),
53
54 #[error("Failed to fetch object '{key}' from storage: {error}")]
56 StorageFetch {
57 key: String,
58 error: String,
59 },
60
61 #[error("Failed to read storage object '{key}': {error}")]
62 StorageRead {
63 key: String,
64 error: String,
65 },
66
67 #[error("Failed to put object '{key}' to storage: {error}")]
68 StoragePut {
69 key: String,
70 error: String,
71 },
72
73 #[error("Failed to delete object '{key}' from storage: {error}")]
74 StorageDelete {
75 key: String,
76 error: String,
77 },
78
79 #[error("Failed to list storage objects with prefix '{prefix}': {error}")]
80 StorageList {
81 prefix: String,
82 error: String,
83 },
84
85 #[error("Storage object not found: '{0}'")]
86 StorageObjectNotFound(String),
87
88 #[error("Failed to export metrics: {0}")]
90 MetricsExport(String),
91
92 #[error("Failed to write log entry: {0}")]
93 LogWrite(String),
94
95 #[error("Failed to export logs: {0}")]
96 LogExport(String),
97
98 #[error("Failed to create trace span: {0}")]
99 TraceSpanCreate(String),
100
101 #[error("Failed to export trace: {0}")]
102 TraceExport(String),
103
104 #[error("Authentication failed: {0}")]
106 AuthFailed(String),
107
108 #[error("Authorization failed: {0}")]
109 AuthorizationFailed(String),
110
111 #[error("Invalid credentials: {0}")]
112 InvalidCredentials(String),
113
114 #[error("Configuration error: {0}")]
116 ConfigError(String),
117
118 #[error("Missing required configuration: {0}")]
119 MissingConfig(String),
120
121 #[error("Invalid configuration value for '{key}': {reason}")]
122 InvalidConfig {
123 key: String,
124 reason: String,
125 },
126
127 #[error("Network error: {0}")]
129 Network(String),
130
131 #[error("Connection error: {0}")]
132 Connection(String),
133
134 #[error("Connection timeout: {0}")]
135 Timeout(String),
136
137 #[error("Authentication error: {0}")]
138 Authentication(String),
139
140 #[error("Serialization error: {0}")]
142 Serialization(String),
143
144 #[error("Deserialization error: {0}")]
145 Deserialization(String),
146
147 #[error("Cloud operation failed: {0}")]
149 OperationFailed(String),
150
151 #[error("Internal error: {0}")]
152 Internal(String),
153}
154
155pub type Result<T> = std::result::Result<T, CloudError>;
157
158impl CloudError {
159 pub fn secret_fetch(name: impl Into<String>, error: impl Into<String>) -> Self {
161 Self::SecretFetch {
162 name: name.into(),
163 error: error.into(),
164 }
165 }
166
167 pub fn secret_create(name: impl Into<String>, error: impl Into<String>) -> Self {
169 Self::SecretCreate {
170 name: name.into(),
171 error: error.into(),
172 }
173 }
174
175 pub fn secret_update(name: impl Into<String>, error: impl Into<String>) -> Self {
177 Self::SecretUpdate {
178 name: name.into(),
179 error: error.into(),
180 }
181 }
182
183 pub fn storage_fetch(key: impl Into<String>, error: impl Into<String>) -> Self {
185 Self::StorageFetch {
186 key: key.into(),
187 error: error.into(),
188 }
189 }
190
191 pub fn storage_put(key: impl Into<String>, error: impl Into<String>) -> Self {
193 Self::StoragePut {
194 key: key.into(),
195 error: error.into(),
196 }
197 }
198
199 pub fn secret_delete(name: impl Into<String>, error: impl Into<String>) -> Self {
201 Self::SecretDelete {
202 name: name.into(),
203 error: error.into(),
204 }
205 }
206
207 pub fn storage_delete(key: impl Into<String>, error: impl Into<String>) -> Self {
209 Self::StorageDelete {
210 key: key.into(),
211 error: error.into(),
212 }
213 }
214
215 pub fn storage_read(key: impl Into<String>, error: impl Into<String>) -> Self {
217 Self::StorageRead {
218 key: key.into(),
219 error: error.into(),
220 }
221 }
222
223 pub fn storage_get(key: impl Into<String>, error: impl Into<String>) -> Self {
225 Self::StorageFetch {
226 key: key.into(),
227 error: error.into(),
228 }
229 }
230
231 pub fn storage_list(prefix: impl Into<String>, error: impl Into<String>) -> Self {
233 Self::StorageList {
234 prefix: prefix.into(),
235 error: error.into(),
236 }
237 }
238}
239
240impl From<anyhow::Error> for CloudError {
242 fn from(err: anyhow::Error) -> Self {
243 CloudError::Internal(err.to_string())
244 }
245}
246
247impl From<serde_json::Error> for CloudError {
249 fn from(err: serde_json::Error) -> Self {
250 CloudError::Serialization(err.to_string())
251 }
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_error_display() {
260 let err = CloudError::secret_fetch("my-secret", "connection refused");
261 assert_eq!(
262 err.to_string(),
263 "Failed to fetch secret 'my-secret': connection refused"
264 );
265 }
266
267 #[test]
268 fn test_secret_not_found() {
269 let err = CloudError::SecretNotFound("test-secret".to_string());
270 assert!(err.to_string().contains("Secret not found"));
271 }
272
273 #[test]
274 fn test_provider_not_enabled() {
275 let err = CloudError::ProviderNotEnabled("AWS".to_string());
276 assert!(err.to_string().contains("not enabled"));
277 assert!(err.to_string().contains("feature flag"));
278 }
279
280 #[test]
281 fn test_from_serde_error() {
282 let json_err = serde_json::from_str::<serde_json::Value>("{invalid}")
283 .unwrap_err();
284 let cloud_err: CloudError = json_err.into();
285 assert!(matches!(cloud_err, CloudError::Serialization(_)));
286 }
287}