iam_rs/core/
resource.rs

1use crate::validation::{Validate, ValidationContext, ValidationResult, helpers};
2use serde::{Deserialize, Serialize};
3
4/// Represents a resource in an IAM policy
5#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
6#[serde(untagged)]
7pub enum Resource {
8    /// A single resource (e.g., "arn:aws:s3:::bucket/*")
9    Single(String),
10    /// Multiple resources
11    Multiple(Vec<String>),
12}
13
14impl Validate for Resource {
15    fn validate(&self, context: &mut ValidationContext) -> ValidationResult {
16        context.with_segment("Resource", |ctx| match self {
17            Resource::Single(resource) => helpers::validate_resource(resource, ctx),
18            Resource::Multiple(resources) => {
19                if resources.is_empty() {
20                    return Err(crate::validation::ValidationError::InvalidValue {
21                        field: "Resource".to_string(),
22                        value: "[]".to_string(),
23                        reason: "Resource list cannot be empty".to_string(),
24                    });
25                }
26
27                let results: Vec<ValidationResult> = resources
28                    .iter()
29                    .enumerate()
30                    .map(|(i, resource)| {
31                        ctx.with_segment(&format!("[{}]", i), |nested_ctx| {
32                            helpers::validate_resource(resource, nested_ctx)
33                        })
34                    })
35                    .collect();
36
37                helpers::collect_errors(results)
38            }
39        })
40    }
41}
42
43#[cfg(test)]
44mod tests {
45    use super::*;
46
47    #[test]
48    fn test_resource_validation() {
49        let valid_single = Resource::Single("arn:aws:s3:::bucket/*".to_string());
50        assert!(valid_single.is_valid());
51
52        let valid_wildcard = Resource::Single("*".to_string());
53        assert!(valid_wildcard.is_valid());
54
55        let valid_multiple = Resource::Multiple(vec![
56            "arn:aws:s3:::bucket/*".to_string(),
57            "arn:aws:s3:::other-bucket/*".to_string(),
58        ]);
59        assert!(valid_multiple.is_valid());
60
61        let invalid_single = Resource::Single("invalid-resource".to_string());
62        assert!(!invalid_single.is_valid());
63
64        let empty_multiple = Resource::Multiple(vec![]);
65        assert!(!empty_multiple.is_valid());
66
67        let invalid_multiple = Resource::Multiple(vec![
68            "arn:aws:s3:::bucket/*".to_string(),
69            "invalid-resource".to_string(),
70        ]);
71        assert!(!invalid_multiple.is_valid());
72    }
73
74    #[test]
75    fn test_resource_with_wildcards() {
76        let wildcard_resource = Resource::Single("arn:aws:s3:::*/*".to_string());
77        assert!(wildcard_resource.is_valid());
78
79        let complex_wildcard =
80            Resource::Single("arn:aws:s3:::bucket/folder/*/file.txt".to_string());
81        assert!(complex_wildcard.is_valid());
82    }
83}