1use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize, Serialize)]
10#[serde(rename_all = "lowercase")]
11pub enum CloudProvider {
12 AWS,
14 GCP,
16 Azure,
18 None,
20}
21
22impl CloudProvider {
23 pub fn as_str(&self) -> &str {
25 match self {
26 CloudProvider::AWS => "aws",
27 CloudProvider::GCP => "gcp",
28 CloudProvider::Azure => "azure",
29 CloudProvider::None => "none",
30 }
31 }
32
33 pub fn is_enabled(&self) -> bool {
35 !matches!(self, CloudProvider::None)
36 }
37}
38
39#[derive(Debug, Clone, Deserialize, Serialize)]
41pub struct CloudConfig {
42 #[serde(default = "default_provider")]
44 pub provider: CloudProvider,
45
46 #[serde(default)]
48 pub aws: AwsConfig,
49
50 #[serde(default)]
52 pub gcp: GcpConfig,
53
54 #[serde(default)]
56 pub azure: AzureConfig,
57}
58
59fn default_provider() -> CloudProvider {
60 CloudProvider::None
61}
62
63impl Default for CloudConfig {
64 fn default() -> Self {
65 Self {
66 provider: CloudProvider::None,
67 aws: AwsConfig::default(),
68 gcp: GcpConfig::default(),
69 azure: AzureConfig::default(),
70 }
71 }
72}
73
74#[derive(Debug, Clone, Default, Deserialize, Serialize)]
80pub struct AwsConfig {
81 #[serde(default = "default_aws_region")]
83 pub region: String,
84
85 #[serde(default)]
87 pub secrets_manager: AwsSecretsManagerConfig,
88
89 #[serde(default)]
91 pub s3: AwsS3Config,
92
93 #[serde(default)]
95 pub cloudwatch: AwsCloudWatchConfig,
96
97 #[serde(default)]
99 pub xray: AwsXRayConfig,
100}
101
102fn default_aws_region() -> String {
103 "us-east-1".to_string()
104}
105
106#[derive(Debug, Clone, Deserialize, Serialize)]
108pub struct AwsSecretsManagerConfig {
109 #[serde(default)]
111 pub enabled: bool,
112
113 #[serde(default = "default_cache_ttl")]
115 pub cache_ttl_seconds: u64,
116}
117
118impl Default for AwsSecretsManagerConfig {
119 fn default() -> Self {
120 Self {
121 enabled: false,
122 cache_ttl_seconds: 300,
123 }
124 }
125}
126
127fn default_cache_ttl() -> u64 {
128 300
129}
130
131#[derive(Debug, Clone, Default, Deserialize, Serialize)]
133pub struct AwsS3Config {
134 #[serde(default)]
136 pub bucket: String,
137
138 #[serde(default = "default_models_prefix")]
140 pub models_prefix: String,
141
142 #[serde(default = "default_results_prefix")]
144 pub results_prefix: String,
145}
146
147fn default_models_prefix() -> String {
148 "models/".to_string()
149}
150
151fn default_results_prefix() -> String {
152 "scan-results/".to_string()
153}
154
155#[derive(Debug, Clone, Deserialize, Serialize)]
157pub struct AwsCloudWatchConfig {
158 #[serde(default)]
160 pub enabled: bool,
161
162 #[serde(default = "default_cw_namespace")]
164 pub namespace: String,
165
166 #[serde(default = "default_cw_log_group")]
168 pub log_group: String,
169}
170
171impl Default for AwsCloudWatchConfig {
172 fn default() -> Self {
173 Self {
174 enabled: false,
175 namespace: "LLMShield".to_string(),
176 log_group: "/llm-shield/api".to_string(),
177 }
178 }
179}
180
181fn default_cw_namespace() -> String {
182 "LLMShield".to_string()
183}
184
185fn default_cw_log_group() -> String {
186 "/llm-shield/api".to_string()
187}
188
189#[derive(Debug, Clone, Deserialize, Serialize)]
191pub struct AwsXRayConfig {
192 #[serde(default)]
194 pub enabled: bool,
195}
196
197impl Default for AwsXRayConfig {
198 fn default() -> Self {
199 Self { enabled: false }
200 }
201}
202
203#[derive(Debug, Clone, Default, Deserialize, Serialize)]
209pub struct GcpConfig {
210 #[serde(default)]
212 pub project_id: String,
213
214 #[serde(default)]
216 pub secret_manager: GcpSecretManagerConfig,
217
218 #[serde(default)]
220 pub cloud_storage: GcpCloudStorageConfig,
221
222 #[serde(default)]
224 pub cloud_logging: GcpCloudLoggingConfig,
225
226 #[serde(default)]
228 pub cloud_trace: GcpCloudTraceConfig,
229}
230
231#[derive(Debug, Clone, Deserialize, Serialize)]
233pub struct GcpSecretManagerConfig {
234 #[serde(default)]
236 pub enabled: bool,
237
238 #[serde(default = "default_cache_ttl")]
240 pub cache_ttl_seconds: u64,
241}
242
243impl Default for GcpSecretManagerConfig {
244 fn default() -> Self {
245 Self {
246 enabled: false,
247 cache_ttl_seconds: 300,
248 }
249 }
250}
251
252#[derive(Debug, Clone, Default, Deserialize, Serialize)]
254pub struct GcpCloudStorageConfig {
255 #[serde(default)]
257 pub bucket: String,
258}
259
260#[derive(Debug, Clone, Deserialize, Serialize)]
262pub struct GcpCloudLoggingConfig {
263 #[serde(default)]
265 pub enabled: bool,
266
267 #[serde(default = "default_gcp_log_name")]
269 pub log_name: String,
270}
271
272impl Default for GcpCloudLoggingConfig {
273 fn default() -> Self {
274 Self {
275 enabled: false,
276 log_name: "llm-shield-api".to_string(),
277 }
278 }
279}
280
281fn default_gcp_log_name() -> String {
282 "llm-shield-api".to_string()
283}
284
285#[derive(Debug, Clone, Deserialize, Serialize)]
287pub struct GcpCloudTraceConfig {
288 #[serde(default)]
290 pub enabled: bool,
291}
292
293impl Default for GcpCloudTraceConfig {
294 fn default() -> Self {
295 Self { enabled: false }
296 }
297}
298
299#[derive(Debug, Clone, Default, Deserialize, Serialize)]
305pub struct AzureConfig {
306 #[serde(default)]
308 pub subscription_id: String,
309
310 #[serde(default)]
312 pub resource_group: String,
313
314 #[serde(default)]
316 pub key_vault: AzureKeyVaultConfig,
317
318 #[serde(default)]
320 pub blob_storage: AzureBlobStorageConfig,
321
322 #[serde(default)]
324 pub monitor: AzureMonitorConfig,
325
326 #[serde(default)]
328 pub application_insights: AzureApplicationInsightsConfig,
329}
330
331#[derive(Debug, Clone, Deserialize, Serialize)]
333pub struct AzureKeyVaultConfig {
334 #[serde(default)]
336 pub vault_url: String,
337
338 #[serde(default = "default_cache_ttl")]
340 pub cache_ttl_seconds: u64,
341}
342
343impl Default for AzureKeyVaultConfig {
344 fn default() -> Self {
345 Self {
346 vault_url: String::new(),
347 cache_ttl_seconds: 300,
348 }
349 }
350}
351
352#[derive(Debug, Clone, Default, Deserialize, Serialize)]
354pub struct AzureBlobStorageConfig {
355 #[serde(default)]
357 pub account: String,
358
359 #[serde(default)]
361 pub container: String,
362}
363
364#[derive(Debug, Clone, Deserialize, Serialize)]
366pub struct AzureMonitorConfig {
367 #[serde(default)]
369 pub enabled: bool,
370
371 #[serde(default)]
373 pub workspace_id: String,
374}
375
376impl Default for AzureMonitorConfig {
377 fn default() -> Self {
378 Self {
379 enabled: false,
380 workspace_id: String::new(),
381 }
382 }
383}
384
385#[derive(Debug, Clone, Deserialize, Serialize)]
387pub struct AzureApplicationInsightsConfig {
388 #[serde(default)]
390 pub instrumentation_key: String,
391
392 #[serde(default)]
394 pub enabled: bool,
395}
396
397impl Default for AzureApplicationInsightsConfig {
398 fn default() -> Self {
399 Self {
400 instrumentation_key: String::new(),
401 enabled: false,
402 }
403 }
404}
405
406#[cfg(test)]
407mod tests {
408 use super::*;
409
410 #[test]
411 fn test_cloud_provider_as_str() {
412 assert_eq!(CloudProvider::AWS.as_str(), "aws");
413 assert_eq!(CloudProvider::GCP.as_str(), "gcp");
414 assert_eq!(CloudProvider::Azure.as_str(), "azure");
415 assert_eq!(CloudProvider::None.as_str(), "none");
416 }
417
418 #[test]
419 fn test_cloud_provider_is_enabled() {
420 assert!(CloudProvider::AWS.is_enabled());
421 assert!(CloudProvider::GCP.is_enabled());
422 assert!(CloudProvider::Azure.is_enabled());
423 assert!(!CloudProvider::None.is_enabled());
424 }
425
426 #[test]
427 fn test_cloud_config_default() {
428 let config = CloudConfig::default();
429 assert_eq!(config.provider, CloudProvider::None);
430 assert!(!config.provider.is_enabled());
431 }
432
433 #[test]
434 fn test_aws_config_defaults() {
435 let config = AwsConfig::default();
436 assert_eq!(config.region, "us-east-1");
437 assert!(!config.secrets_manager.enabled);
438 assert_eq!(config.secrets_manager.cache_ttl_seconds, 300);
439 }
440
441 #[test]
442 fn test_config_deserialization() {
443 let yaml = r#"
444provider: aws
445aws:
446 region: us-west-2
447 secrets_manager:
448 enabled: true
449 cache_ttl_seconds: 600
450 s3:
451 bucket: my-bucket
452"#;
453
454 let config: CloudConfig = serde_yaml::from_str(yaml).unwrap();
455 assert_eq!(config.provider, CloudProvider::AWS);
456 assert_eq!(config.aws.region, "us-west-2");
457 assert!(config.aws.secrets_manager.enabled);
458 assert_eq!(config.aws.secrets_manager.cache_ttl_seconds, 600);
459 assert_eq!(config.aws.s3.bucket, "my-bucket");
460 }
461
462 #[test]
463 fn test_config_serialization() {
464 let config = CloudConfig {
465 provider: CloudProvider::GCP,
466 gcp: GcpConfig {
467 project_id: "my-project".to_string(),
468 secret_manager: GcpSecretManagerConfig {
469 enabled: true,
470 cache_ttl_seconds: 300,
471 },
472 ..Default::default()
473 },
474 ..Default::default()
475 };
476
477 let yaml = serde_yaml::to_string(&config).unwrap();
478 assert!(yaml.contains("provider: gcp"));
479 assert!(yaml.contains("project_id: my-project"));
480 }
481}