llm_shield_cloud/
error.rs

1//! Error types for cloud integrations.
2//!
3//! Provides unified error handling across all cloud providers (AWS, GCP, Azure).
4
5use thiserror::Error;
6
7/// Unified cloud error type for all cloud operations.
8#[derive(Error, Debug)]
9pub enum CloudError {
10    // Initialization errors
11    #[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    // Secret management errors
18    #[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    // Storage errors
55    #[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    // Observability errors
89    #[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    // Authentication errors
105    #[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    // Configuration errors
115    #[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    // Network errors
128    #[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    // Serialization errors
141    #[error("Serialization error: {0}")]
142    Serialization(String),
143
144    #[error("Deserialization error: {0}")]
145    Deserialization(String),
146
147    // Generic errors
148    #[error("Cloud operation failed: {0}")]
149    OperationFailed(String),
150
151    #[error("Internal error: {0}")]
152    Internal(String),
153}
154
155/// Result type alias for cloud operations.
156pub type Result<T> = std::result::Result<T, CloudError>;
157
158impl CloudError {
159    /// Creates a new `SecretFetch` error.
160    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    /// Creates a new `SecretCreate` error.
168    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    /// Creates a new `SecretUpdate` error.
176    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    /// Creates a new `StorageFetch` error.
184    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    /// Creates a new `StoragePut` error.
192    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    /// Creates a new `SecretDelete` error.
200    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    /// Creates a new `StorageDelete` error.
208    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    /// Creates a new `StorageRead` error.
216    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    /// Alias for storage_fetch (for compatibility).
224    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    /// Creates a new `StorageList` error.
232    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
240// Conversion from anyhow::Error
241impl From<anyhow::Error> for CloudError {
242    fn from(err: anyhow::Error) -> Self {
243        CloudError::Internal(err.to_string())
244    }
245}
246
247// Conversion from serde_json::Error
248impl 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}