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 create trace span: {0}")]
96    TraceSpanCreate(String),
97
98    #[error("Failed to export trace: {0}")]
99    TraceExport(String),
100
101    // Authentication errors
102    #[error("Authentication failed: {0}")]
103    AuthFailed(String),
104
105    #[error("Authorization failed: {0}")]
106    AuthorizationFailed(String),
107
108    #[error("Invalid credentials: {0}")]
109    InvalidCredentials(String),
110
111    // Configuration errors
112    #[error("Configuration error: {0}")]
113    ConfigError(String),
114
115    #[error("Missing required configuration: {0}")]
116    MissingConfig(String),
117
118    #[error("Invalid configuration value for '{key}': {reason}")]
119    InvalidConfig {
120        key: String,
121        reason: String,
122    },
123
124    // Network errors
125    #[error("Network error: {0}")]
126    Network(String),
127
128    #[error("Connection timeout: {0}")]
129    Timeout(String),
130
131    // Serialization errors
132    #[error("Serialization error: {0}")]
133    Serialization(String),
134
135    #[error("Deserialization error: {0}")]
136    Deserialization(String),
137
138    // Generic errors
139    #[error("Cloud operation failed: {0}")]
140    OperationFailed(String),
141
142    #[error("Internal error: {0}")]
143    Internal(String),
144}
145
146/// Result type alias for cloud operations.
147pub type Result<T> = std::result::Result<T, CloudError>;
148
149impl CloudError {
150    /// Creates a new `SecretFetch` error.
151    pub fn secret_fetch(name: impl Into<String>, error: impl Into<String>) -> Self {
152        Self::SecretFetch {
153            name: name.into(),
154            error: error.into(),
155        }
156    }
157
158    /// Creates a new `SecretCreate` error.
159    pub fn secret_create(name: impl Into<String>, error: impl Into<String>) -> Self {
160        Self::SecretCreate {
161            name: name.into(),
162            error: error.into(),
163        }
164    }
165
166    /// Creates a new `SecretUpdate` error.
167    pub fn secret_update(name: impl Into<String>, error: impl Into<String>) -> Self {
168        Self::SecretUpdate {
169            name: name.into(),
170            error: error.into(),
171        }
172    }
173
174    /// Creates a new `StorageFetch` error.
175    pub fn storage_fetch(key: impl Into<String>, error: impl Into<String>) -> Self {
176        Self::StorageFetch {
177            key: key.into(),
178            error: error.into(),
179        }
180    }
181
182    /// Creates a new `StoragePut` error.
183    pub fn storage_put(key: impl Into<String>, error: impl Into<String>) -> Self {
184        Self::StoragePut {
185            key: key.into(),
186            error: error.into(),
187        }
188    }
189}
190
191// Conversion from anyhow::Error
192impl From<anyhow::Error> for CloudError {
193    fn from(err: anyhow::Error) -> Self {
194        CloudError::Internal(err.to_string())
195    }
196}
197
198// Conversion from serde_json::Error
199impl From<serde_json::Error> for CloudError {
200    fn from(err: serde_json::Error) -> Self {
201        CloudError::Serialization(err.to_string())
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208
209    #[test]
210    fn test_error_display() {
211        let err = CloudError::secret_fetch("my-secret", "connection refused");
212        assert_eq!(
213            err.to_string(),
214            "Failed to fetch secret 'my-secret': connection refused"
215        );
216    }
217
218    #[test]
219    fn test_secret_not_found() {
220        let err = CloudError::SecretNotFound("test-secret".to_string());
221        assert!(err.to_string().contains("Secret not found"));
222    }
223
224    #[test]
225    fn test_provider_not_enabled() {
226        let err = CloudError::ProviderNotEnabled("AWS".to_string());
227        assert!(err.to_string().contains("not enabled"));
228        assert!(err.to_string().contains("feature flag"));
229    }
230
231    #[test]
232    fn test_from_serde_error() {
233        let json_err = serde_json::from_str::<serde_json::Value>("{invalid}")
234            .unwrap_err();
235        let cloud_err: CloudError = json_err.into();
236        assert!(matches!(cloud_err, CloudError::Serialization(_)));
237    }
238}