iam_rs/core/
operator.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
4#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
5pub enum OperatorType {
6    String,
7    Numeric,
8    Date,
9    Boolean,
10    Binary,
11    IpAddress,
12    Arn,
13    Null,
14}
15
16/// Represents the different types of condition operators available in IAM policies
17#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash, PartialOrd, Ord)]
18#[serde(rename_all = "PascalCase")]
19#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
20pub enum IAMOperator {
21    // String condition operators
22    #[serde(rename = "StringEquals")]
23    StringEquals,
24    #[serde(rename = "StringNotEquals")]
25    StringNotEquals,
26    #[serde(rename = "StringEqualsIgnoreCase")]
27    StringEqualsIgnoreCase,
28    #[serde(rename = "StringNotEqualsIgnoreCase")]
29    StringNotEqualsIgnoreCase,
30    #[serde(rename = "StringLike")]
31    StringLike,
32    #[serde(rename = "StringNotLike")]
33    StringNotLike,
34
35    // Multivalued string condition operators
36    #[serde(rename = "ForAllValues:StringEquals")]
37    ForAllValuesStringEquals,
38    #[serde(rename = "ForAllValues:StringEqualsIgnoreCase")]
39    ForAllValuesStringEqualsIgnoreCase,
40    #[serde(rename = "ForAnyValue:StringEquals")]
41    ForAnyValueStringEquals,
42    #[serde(rename = "ForAnyValue:StringEqualsIgnoreCase")]
43    ForAnyValueStringEqualsIgnoreCase,
44    #[serde(rename = "ForAllValues:StringNotEquals")]
45    ForAllValuesStringNotEquals,
46    #[serde(rename = "ForAllValues:StringNotEqualsIgnoreCase")]
47    ForAllValuesStringNotEqualsIgnoreCase,
48    #[serde(rename = "ForAnyValue:StringNotEquals")]
49    ForAnyValueStringNotEquals,
50    #[serde(rename = "ForAnyValue:StringNotEqualsIgnoreCase")]
51    ForAnyValueStringNotEqualsIgnoreCase,
52    #[serde(rename = "ForAllValues:StringLike")]
53    ForAllValuesStringLike,
54    #[serde(rename = "ForAnyValue:StringLike")]
55    ForAnyValueStringLike,
56    #[serde(rename = "ForAllValues:StringNotLike")]
57    ForAllValuesStringNotLike,
58    #[serde(rename = "ForAnyValue:StringNotLike")]
59    ForAnyValueStringNotLike,
60
61    // Numeric condition operators
62    #[serde(rename = "NumericEquals")]
63    NumericEquals,
64    #[serde(rename = "NumericNotEquals")]
65    NumericNotEquals,
66    #[serde(rename = "NumericLessThan")]
67    NumericLessThan,
68    #[serde(rename = "NumericLessThanEquals")]
69    NumericLessThanEquals,
70    #[serde(rename = "NumericGreaterThan")]
71    NumericGreaterThan,
72    #[serde(rename = "NumericGreaterThanEquals")]
73    NumericGreaterThanEquals,
74
75    // Date condition operators
76    #[serde(rename = "DateEquals")]
77    DateEquals,
78    #[serde(rename = "DateNotEquals")]
79    DateNotEquals,
80    #[serde(rename = "DateLessThan")]
81    DateLessThan,
82    #[serde(rename = "DateLessThanEquals")]
83    DateLessThanEquals,
84    #[serde(rename = "DateGreaterThan")]
85    DateGreaterThan,
86    #[serde(rename = "DateGreaterThanEquals")]
87    DateGreaterThanEquals,
88
89    // Boolean condition operators
90    #[serde(rename = "Bool")]
91    Bool,
92    #[serde(rename = "ForAllValues:Bool")]
93    ForAllValuesBool,
94    #[serde(rename = "ForAnyValue:Bool")]
95    ForAnyValueBool,
96
97    // Binary condition operators
98    #[serde(rename = "BinaryEquals")]
99    BinaryEquals,
100
101    // IP address condition operators
102    #[serde(rename = "IpAddress")]
103    IpAddress,
104    #[serde(rename = "NotIpAddress")]
105    NotIpAddress,
106
107    // ARN condition operators
108    #[serde(rename = "ArnEquals")]
109    ArnEquals,
110    #[serde(rename = "ArnLike")]
111    ArnLike,
112    #[serde(rename = "ArnNotEquals")]
113    ArnNotEquals,
114    #[serde(rename = "ArnNotLike")]
115    ArnNotLike,
116
117    // Multivalued ARN condition operators
118    #[serde(rename = "ForAllValues:ArnEquals")]
119    ForAllValuesArnEquals,
120    #[serde(rename = "ForAllValues:ArnLike")]
121    ForAllValuesArnLike,
122    #[serde(rename = "ForAnyValue:ArnEquals")]
123    ForAnyValueArnEquals,
124    #[serde(rename = "ForAnyValue:ArnLike")]
125    ForAnyValueArnLike,
126    #[serde(rename = "ForAllValues:ArnNotEquals")]
127    ForAllValuesArnNotEquals,
128    #[serde(rename = "ForAllValues:ArnNotLike")]
129    ForAllValuesArnNotLike,
130    #[serde(rename = "ForAnyValue:ArnNotEquals")]
131    ForAnyValueArnNotEquals,
132    #[serde(rename = "ForAnyValue:ArnNotLike")]
133    ForAnyValueArnNotLike,
134
135    // Null condition operator
136    #[serde(rename = "Null")]
137    Null,
138
139    // IfExists variants (can be applied to most operators)
140    #[serde(rename = "StringEqualsIfExists")]
141    StringEqualsIfExists,
142    #[serde(rename = "StringNotEqualsIfExists")]
143    StringNotEqualsIfExists,
144    #[serde(rename = "StringEqualsIgnoreCaseIfExists")]
145    StringEqualsIgnoreCaseIfExists,
146    #[serde(rename = "StringNotEqualsIgnoreCaseIfExists")]
147    StringNotEqualsIgnoreCaseIfExists,
148    #[serde(rename = "StringLikeIfExists")]
149    StringLikeIfExists,
150    #[serde(rename = "StringNotLikeIfExists")]
151    StringNotLikeIfExists,
152    #[serde(rename = "NumericEqualsIfExists")]
153    NumericEqualsIfExists,
154    #[serde(rename = "NumericNotEqualsIfExists")]
155    NumericNotEqualsIfExists,
156    #[serde(rename = "NumericLessThanIfExists")]
157    NumericLessThanIfExists,
158    #[serde(rename = "NumericLessThanEqualsIfExists")]
159    NumericLessThanEqualsIfExists,
160    #[serde(rename = "NumericGreaterThanIfExists")]
161    NumericGreaterThanIfExists,
162    #[serde(rename = "NumericGreaterThanEqualsIfExists")]
163    NumericGreaterThanEqualsIfExists,
164    #[serde(rename = "DateEqualsIfExists")]
165    DateEqualsIfExists,
166    #[serde(rename = "DateNotEqualsIfExists")]
167    DateNotEqualsIfExists,
168    #[serde(rename = "DateLessThanIfExists")]
169    DateLessThanIfExists,
170    #[serde(rename = "DateLessThanEqualsIfExists")]
171    DateLessThanEqualsIfExists,
172    #[serde(rename = "DateGreaterThanIfExists")]
173    DateGreaterThanIfExists,
174    #[serde(rename = "DateGreaterThanEqualsIfExists")]
175    DateGreaterThanEqualsIfExists,
176    #[serde(rename = "BoolIfExists")]
177    BoolIfExists,
178    #[serde(rename = "BinaryEqualsIfExists")]
179    BinaryEqualsIfExists,
180    #[serde(rename = "IpAddressIfExists")]
181    IpAddressIfExists,
182    #[serde(rename = "NotIpAddressIfExists")]
183    NotIpAddressIfExists,
184    #[serde(rename = "ArnEqualsIfExists")]
185    ArnEqualsIfExists,
186    #[serde(rename = "ArnLikeIfExists")]
187    ArnLikeIfExists,
188    #[serde(rename = "ArnNotEqualsIfExists")]
189    ArnNotEqualsIfExists,
190    #[serde(rename = "ArnNotLikeIfExists")]
191    ArnNotLikeIfExists,
192}
193
194impl IAMOperator {
195    /// Returns true if this operator is a string-based operator
196    #[must_use]
197    pub fn is_string_operator(&self) -> bool {
198        matches!(
199            self,
200            IAMOperator::StringEquals
201                | IAMOperator::StringNotEquals
202                | IAMOperator::StringEqualsIgnoreCase
203                | IAMOperator::StringNotEqualsIgnoreCase
204                | IAMOperator::StringLike
205                | IAMOperator::StringNotLike
206                | IAMOperator::ForAllValuesStringEquals
207                | IAMOperator::ForAllValuesStringEqualsIgnoreCase
208                | IAMOperator::ForAnyValueStringEquals
209                | IAMOperator::ForAnyValueStringEqualsIgnoreCase
210                | IAMOperator::ForAllValuesStringNotEquals
211                | IAMOperator::ForAllValuesStringNotEqualsIgnoreCase
212                | IAMOperator::ForAnyValueStringNotEquals
213                | IAMOperator::ForAnyValueStringNotEqualsIgnoreCase
214                | IAMOperator::ForAllValuesStringLike
215                | IAMOperator::ForAnyValueStringLike
216                | IAMOperator::ForAllValuesStringNotLike
217                | IAMOperator::ForAnyValueStringNotLike
218                | IAMOperator::StringEqualsIfExists
219                | IAMOperator::StringNotEqualsIfExists
220                | IAMOperator::StringEqualsIgnoreCaseIfExists
221                | IAMOperator::StringNotEqualsIgnoreCaseIfExists
222                | IAMOperator::StringLikeIfExists
223                | IAMOperator::StringNotLikeIfExists
224        )
225    }
226
227    /// Returns true if this operator is a numeric-based operator
228    #[must_use]
229    pub fn is_numeric_operator(&self) -> bool {
230        matches!(
231            self,
232            IAMOperator::NumericEquals
233                | IAMOperator::NumericNotEquals
234                | IAMOperator::NumericLessThan
235                | IAMOperator::NumericLessThanEquals
236                | IAMOperator::NumericGreaterThan
237                | IAMOperator::NumericGreaterThanEquals
238                | IAMOperator::NumericEqualsIfExists
239                | IAMOperator::NumericNotEqualsIfExists
240                | IAMOperator::NumericLessThanIfExists
241                | IAMOperator::NumericLessThanEqualsIfExists
242                | IAMOperator::NumericGreaterThanIfExists
243                | IAMOperator::NumericGreaterThanEqualsIfExists
244        )
245    }
246
247    /// Returns true if this operator is a date-based operator
248    #[must_use]
249    pub fn is_date_operator(&self) -> bool {
250        matches!(
251            self,
252            IAMOperator::DateEquals
253                | IAMOperator::DateNotEquals
254                | IAMOperator::DateLessThan
255                | IAMOperator::DateLessThanEquals
256                | IAMOperator::DateGreaterThan
257                | IAMOperator::DateGreaterThanEquals
258                | IAMOperator::DateEqualsIfExists
259                | IAMOperator::DateNotEqualsIfExists
260                | IAMOperator::DateLessThanIfExists
261                | IAMOperator::DateLessThanEqualsIfExists
262                | IAMOperator::DateGreaterThanIfExists
263                | IAMOperator::DateGreaterThanEqualsIfExists
264        )
265    }
266
267    /// Returns true if this operator is a boolean-based operator
268    #[must_use]
269    pub fn is_boolean_operator(&self) -> bool {
270        matches!(
271            self,
272            IAMOperator::Bool
273                | IAMOperator::ForAllValuesBool
274                | IAMOperator::ForAnyValueBool
275                | IAMOperator::BoolIfExists
276        )
277    }
278
279    /// Returns true if this operator is an ARN-based operator
280    #[must_use]
281    pub fn is_arn_operator(&self) -> bool {
282        matches!(
283            self,
284            IAMOperator::ArnEquals
285                | IAMOperator::ArnLike
286                | IAMOperator::ArnNotEquals
287                | IAMOperator::ArnNotLike
288                | IAMOperator::ForAllValuesArnEquals
289                | IAMOperator::ForAllValuesArnLike
290                | IAMOperator::ForAnyValueArnEquals
291                | IAMOperator::ForAnyValueArnLike
292                | IAMOperator::ForAllValuesArnNotEquals
293                | IAMOperator::ForAllValuesArnNotLike
294                | IAMOperator::ForAnyValueArnNotEquals
295                | IAMOperator::ForAnyValueArnNotLike
296                | IAMOperator::ArnEqualsIfExists
297                | IAMOperator::ArnLikeIfExists
298                | IAMOperator::ArnNotEqualsIfExists
299                | IAMOperator::ArnNotLikeIfExists
300        )
301    }
302
303    /// Returns true if this operator is an IP address-based operator
304    #[must_use]
305    pub fn is_ip_operator(&self) -> bool {
306        matches!(
307            self,
308            IAMOperator::IpAddress
309                | IAMOperator::NotIpAddress
310                | IAMOperator::IpAddressIfExists
311                | IAMOperator::NotIpAddressIfExists
312        )
313    }
314
315    /// Returns true if this operator is a binary-based operator
316    #[must_use]
317    pub fn is_binary_operator(&self) -> bool {
318        matches!(
319            self,
320            IAMOperator::BinaryEquals | IAMOperator::BinaryEqualsIfExists
321        )
322    }
323
324    /// Returns true if this operator supports wildcards
325    #[must_use]
326    pub fn supports_wildcards(&self) -> bool {
327        matches!(
328            self,
329            IAMOperator::StringLike
330                | IAMOperator::StringNotLike
331                | IAMOperator::ForAllValuesStringLike
332                | IAMOperator::ForAnyValueStringLike
333                | IAMOperator::ForAllValuesStringNotLike
334                | IAMOperator::ForAnyValueStringNotLike
335                | IAMOperator::StringLikeIfExists
336                | IAMOperator::StringNotLikeIfExists
337                | IAMOperator::ArnEquals
338                | IAMOperator::ArnLike
339                | IAMOperator::ArnNotEquals
340                | IAMOperator::ArnNotLike
341                | IAMOperator::ForAllValuesArnEquals
342                | IAMOperator::ForAllValuesArnLike
343                | IAMOperator::ForAnyValueArnEquals
344                | IAMOperator::ForAnyValueArnLike
345                | IAMOperator::ForAllValuesArnNotEquals
346                | IAMOperator::ForAllValuesArnNotLike
347                | IAMOperator::ForAnyValueArnNotEquals
348                | IAMOperator::ForAnyValueArnNotLike
349                | IAMOperator::ArnEqualsIfExists
350                | IAMOperator::ArnLikeIfExists
351                | IAMOperator::ArnNotEqualsIfExists
352                | IAMOperator::ArnNotLikeIfExists
353        )
354    }
355
356    /// Returns true if this operator supports policy variables
357    #[must_use]
358    pub fn supports_policy_variables(&self) -> bool {
359        !self.is_numeric_operator()
360            && !self.is_date_operator()
361            && !self.is_binary_operator()
362            && !self.is_ip_operator()
363    }
364
365    /// Returns true if this operator is a multivalued operator (ForAllValues/ForAnyValue)
366    #[must_use]
367    pub fn is_multivalued_operator(&self) -> bool {
368        self.to_string().starts_with("ForAllValues:")
369            || self.to_string().starts_with("ForAnyValue:")
370    }
371
372    /// Returns true if this operator is an "`IfExists`" variant
373    #[must_use]
374    pub fn is_if_exists_operator(&self) -> bool {
375        self.to_string().ends_with("IfExists")
376    }
377
378    /// Returns true if this operator is a negated operator (Not*)
379    #[must_use]
380    pub fn is_negated_operator(&self) -> bool {
381        matches!(
382            self,
383            IAMOperator::StringNotEquals
384                | IAMOperator::StringNotEqualsIgnoreCase
385                | IAMOperator::StringNotLike
386                | IAMOperator::ForAllValuesStringNotEquals
387                | IAMOperator::ForAllValuesStringNotEqualsIgnoreCase
388                | IAMOperator::ForAnyValueStringNotEquals
389                | IAMOperator::ForAnyValueStringNotEqualsIgnoreCase
390                | IAMOperator::ForAllValuesStringNotLike
391                | IAMOperator::ForAnyValueStringNotLike
392                | IAMOperator::NumericNotEquals
393                | IAMOperator::DateNotEquals
394                | IAMOperator::NotIpAddress
395                | IAMOperator::ArnNotEquals
396                | IAMOperator::ArnNotLike
397                | IAMOperator::ForAllValuesArnNotEquals
398                | IAMOperator::ForAllValuesArnNotLike
399                | IAMOperator::ForAnyValueArnNotEquals
400                | IAMOperator::ForAnyValueArnNotLike
401                | IAMOperator::StringNotEqualsIfExists
402                | IAMOperator::StringNotEqualsIgnoreCaseIfExists
403                | IAMOperator::StringNotLikeIfExists
404                | IAMOperator::NumericNotEqualsIfExists
405                | IAMOperator::DateNotEqualsIfExists
406                | IAMOperator::NotIpAddressIfExists
407                | IAMOperator::ArnNotEqualsIfExists
408                | IAMOperator::ArnNotLikeIfExists
409        )
410    }
411
412    /// Returns true if this operator supports multiple values (arrays)
413    /// Most operators in AWS IAM can accept arrays, not just ForAllValues/ForAnyValue
414    #[must_use]
415    pub fn supports_multiple_values(&self) -> bool {
416        // Most operators support multiple values except for these specific ones
417        !matches!(
418            self,
419            IAMOperator::Null | IAMOperator::Bool | IAMOperator::BoolIfExists
420        )
421    }
422
423    /// Returns the operator category as a string
424    /// Determine the category of this operator
425    ///
426    /// # Panics
427    ///
428    /// Panics if the operator is not recognized (this should never happen)
429    #[must_use]
430    pub fn category(&self) -> OperatorType {
431        if self.is_string_operator() {
432            OperatorType::String
433        } else if self.is_numeric_operator() {
434            OperatorType::Numeric
435        } else if self.is_date_operator() {
436            OperatorType::Date
437        } else if self.is_boolean_operator() {
438            OperatorType::Boolean
439        } else if self.is_binary_operator() {
440            OperatorType::Binary
441        } else if self.is_ip_operator() {
442            OperatorType::IpAddress
443        } else if self.is_arn_operator() {
444            OperatorType::Arn
445        } else if matches!(self, IAMOperator::Null) {
446            OperatorType::Null
447        } else {
448            panic!("Unknown operator category for operator: {}", self.as_str())
449        }
450    }
451
452    /// Returns the string representation of the operator for use in JSON
453    #[must_use]
454    pub fn as_str(&self) -> &'static str {
455        match self {
456            IAMOperator::StringEquals => "StringEquals",
457            IAMOperator::StringNotEquals => "StringNotEquals",
458            IAMOperator::StringEqualsIgnoreCase => "StringEqualsIgnoreCase",
459            IAMOperator::StringNotEqualsIgnoreCase => "StringNotEqualsIgnoreCase",
460            IAMOperator::StringLike => "StringLike",
461            IAMOperator::StringNotLike => "StringNotLike",
462            IAMOperator::ForAllValuesStringEquals => "ForAllValues:StringEquals",
463            IAMOperator::ForAllValuesStringEqualsIgnoreCase => {
464                "ForAllValues:StringEqualsIgnoreCase"
465            }
466            IAMOperator::ForAnyValueStringEquals => "ForAnyValue:StringEquals",
467            IAMOperator::ForAnyValueStringEqualsIgnoreCase => "ForAnyValue:StringEqualsIgnoreCase",
468            IAMOperator::ForAllValuesStringNotEquals => "ForAllValues:StringNotEquals",
469            IAMOperator::ForAllValuesStringNotEqualsIgnoreCase => {
470                "ForAllValues:StringNotEqualsIgnoreCase"
471            }
472            IAMOperator::ForAnyValueStringNotEquals => "ForAnyValue:StringNotEquals",
473            IAMOperator::ForAnyValueStringNotEqualsIgnoreCase => {
474                "ForAnyValue:StringNotEqualsIgnoreCase"
475            }
476            IAMOperator::ForAllValuesStringLike => "ForAllValues:StringLike",
477            IAMOperator::ForAnyValueStringLike => "ForAnyValue:StringLike",
478            IAMOperator::ForAllValuesStringNotLike => "ForAllValues:StringNotLike",
479            IAMOperator::ForAnyValueStringNotLike => "ForAnyValue:StringNotLike",
480            IAMOperator::NumericEquals => "NumericEquals",
481            IAMOperator::NumericNotEquals => "NumericNotEquals",
482            IAMOperator::NumericLessThan => "NumericLessThan",
483            IAMOperator::NumericLessThanEquals => "NumericLessThanEquals",
484            IAMOperator::NumericGreaterThan => "NumericGreaterThan",
485            IAMOperator::NumericGreaterThanEquals => "NumericGreaterThanEquals",
486            IAMOperator::DateEquals => "DateEquals",
487            IAMOperator::DateNotEquals => "DateNotEquals",
488            IAMOperator::DateLessThan => "DateLessThan",
489            IAMOperator::DateLessThanEquals => "DateLessThanEquals",
490            IAMOperator::DateGreaterThan => "DateGreaterThan",
491            IAMOperator::DateGreaterThanEquals => "DateGreaterThanEquals",
492            IAMOperator::Bool => "Bool",
493            IAMOperator::ForAllValuesBool => "ForAllValues:Bool",
494            IAMOperator::ForAnyValueBool => "ForAnyValue:Bool",
495            IAMOperator::BinaryEquals => "BinaryEquals",
496            IAMOperator::IpAddress => "IpAddress",
497            IAMOperator::NotIpAddress => "NotIpAddress",
498            IAMOperator::ArnEquals => "ArnEquals",
499            IAMOperator::ArnLike => "ArnLike",
500            IAMOperator::ArnNotEquals => "ArnNotEquals",
501            IAMOperator::ArnNotLike => "ArnNotLike",
502            IAMOperator::ForAllValuesArnEquals => "ForAllValues:ArnEquals",
503            IAMOperator::ForAllValuesArnLike => "ForAllValues:ArnLike",
504            IAMOperator::ForAnyValueArnEquals => "ForAnyValue:ArnEquals",
505            IAMOperator::ForAnyValueArnLike => "ForAnyValue:ArnLike",
506            IAMOperator::ForAllValuesArnNotEquals => "ForAllValues:ArnNotEquals",
507            IAMOperator::ForAllValuesArnNotLike => "ForAllValues:ArnNotLike",
508            IAMOperator::ForAnyValueArnNotEquals => "ForAnyValue:ArnNotEquals",
509            IAMOperator::ForAnyValueArnNotLike => "ForAnyValue:ArnNotLike",
510            IAMOperator::Null => "Null",
511            IAMOperator::StringEqualsIfExists => "StringEqualsIfExists",
512            IAMOperator::StringNotEqualsIfExists => "StringNotEqualsIfExists",
513            IAMOperator::StringEqualsIgnoreCaseIfExists => "StringEqualsIgnoreCaseIfExists",
514            IAMOperator::StringNotEqualsIgnoreCaseIfExists => "StringNotEqualsIgnoreCaseIfExists",
515            IAMOperator::StringLikeIfExists => "StringLikeIfExists",
516            IAMOperator::StringNotLikeIfExists => "StringNotLikeIfExists",
517            IAMOperator::NumericEqualsIfExists => "NumericEqualsIfExists",
518            IAMOperator::NumericNotEqualsIfExists => "NumericNotEqualsIfExists",
519            IAMOperator::NumericLessThanIfExists => "NumericLessThanIfExists",
520            IAMOperator::NumericLessThanEqualsIfExists => "NumericLessThanEqualsIfExists",
521            IAMOperator::NumericGreaterThanIfExists => "NumericGreaterThanIfExists",
522            IAMOperator::NumericGreaterThanEqualsIfExists => "NumericGreaterThanEqualsIfExists",
523            IAMOperator::DateEqualsIfExists => "DateEqualsIfExists",
524            IAMOperator::DateNotEqualsIfExists => "DateNotEqualsIfExists",
525            IAMOperator::DateLessThanIfExists => "DateLessThanIfExists",
526            IAMOperator::DateLessThanEqualsIfExists => "DateLessThanEqualsIfExists",
527            IAMOperator::DateGreaterThanIfExists => "DateGreaterThanIfExists",
528            IAMOperator::DateGreaterThanEqualsIfExists => "DateGreaterThanEqualsIfExists",
529            IAMOperator::BoolIfExists => "BoolIfExists",
530            IAMOperator::BinaryEqualsIfExists => "BinaryEqualsIfExists",
531            IAMOperator::IpAddressIfExists => "IpAddressIfExists",
532            IAMOperator::NotIpAddressIfExists => "NotIpAddressIfExists",
533            IAMOperator::ArnEqualsIfExists => "ArnEqualsIfExists",
534            IAMOperator::ArnLikeIfExists => "ArnLikeIfExists",
535            IAMOperator::ArnNotEqualsIfExists => "ArnNotEqualsIfExists",
536            IAMOperator::ArnNotLikeIfExists => "ArnNotLikeIfExists",
537        }
538    }
539}
540
541impl std::fmt::Display for IAMOperator {
542    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
543        write!(f, "{}", self.as_str())
544    }
545}
546
547impl std::str::FromStr for IAMOperator {
548    type Err = String;
549
550    fn from_str(s: &str) -> Result<Self, Self::Err> {
551        match s {
552            "StringEquals" => Ok(IAMOperator::StringEquals),
553            "StringNotEquals" => Ok(IAMOperator::StringNotEquals),
554            "StringEqualsIgnoreCase" => Ok(IAMOperator::StringEqualsIgnoreCase),
555            "StringNotEqualsIgnoreCase" => Ok(IAMOperator::StringNotEqualsIgnoreCase),
556            "StringLike" => Ok(IAMOperator::StringLike),
557            "StringNotLike" => Ok(IAMOperator::StringNotLike),
558            "ForAllValues:StringEquals" => Ok(IAMOperator::ForAllValuesStringEquals),
559            "ForAllValues:StringEqualsIgnoreCase" => {
560                Ok(IAMOperator::ForAllValuesStringEqualsIgnoreCase)
561            }
562            "ForAnyValue:StringEquals" => Ok(IAMOperator::ForAnyValueStringEquals),
563            "ForAnyValue:StringEqualsIgnoreCase" => {
564                Ok(IAMOperator::ForAnyValueStringEqualsIgnoreCase)
565            }
566            "ForAllValues:StringNotEquals" => Ok(IAMOperator::ForAllValuesStringNotEquals),
567            "ForAllValues:StringNotEqualsIgnoreCase" => {
568                Ok(IAMOperator::ForAllValuesStringNotEqualsIgnoreCase)
569            }
570            "ForAnyValue:StringNotEquals" => Ok(IAMOperator::ForAnyValueStringNotEquals),
571            "ForAnyValue:StringNotEqualsIgnoreCase" => {
572                Ok(IAMOperator::ForAnyValueStringNotEqualsIgnoreCase)
573            }
574            "ForAllValues:StringLike" => Ok(IAMOperator::ForAllValuesStringLike),
575            "ForAnyValue:StringLike" => Ok(IAMOperator::ForAnyValueStringLike),
576            "ForAllValues:StringNotLike" => Ok(IAMOperator::ForAllValuesStringNotLike),
577            "ForAnyValue:StringNotLike" => Ok(IAMOperator::ForAnyValueStringNotLike),
578            "NumericEquals" => Ok(IAMOperator::NumericEquals),
579            "NumericNotEquals" => Ok(IAMOperator::NumericNotEquals),
580            "NumericLessThan" => Ok(IAMOperator::NumericLessThan),
581            "NumericLessThanEquals" => Ok(IAMOperator::NumericLessThanEquals),
582            "NumericGreaterThan" => Ok(IAMOperator::NumericGreaterThan),
583            "NumericGreaterThanEquals" => Ok(IAMOperator::NumericGreaterThanEquals),
584            "DateEquals" => Ok(IAMOperator::DateEquals),
585            "DateNotEquals" => Ok(IAMOperator::DateNotEquals),
586            "DateLessThan" => Ok(IAMOperator::DateLessThan),
587            "DateLessThanEquals" => Ok(IAMOperator::DateLessThanEquals),
588            "DateGreaterThan" => Ok(IAMOperator::DateGreaterThan),
589            "DateGreaterThanEquals" => Ok(IAMOperator::DateGreaterThanEquals),
590            "Bool" => Ok(IAMOperator::Bool),
591            "ForAllValues:Bool" => Ok(IAMOperator::ForAllValuesBool),
592            "ForAnyValue:Bool" => Ok(IAMOperator::ForAnyValueBool),
593            "BinaryEquals" => Ok(IAMOperator::BinaryEquals),
594            "IpAddress" => Ok(IAMOperator::IpAddress),
595            "NotIpAddress" => Ok(IAMOperator::NotIpAddress),
596            "ArnEquals" => Ok(IAMOperator::ArnEquals),
597            "ArnLike" => Ok(IAMOperator::ArnLike),
598            "ArnNotEquals" => Ok(IAMOperator::ArnNotEquals),
599            "ArnNotLike" => Ok(IAMOperator::ArnNotLike),
600            "ForAllValues:ArnEquals" => Ok(IAMOperator::ForAllValuesArnEquals),
601            "ForAllValues:ArnLike" => Ok(IAMOperator::ForAllValuesArnLike),
602            "ForAnyValue:ArnEquals" => Ok(IAMOperator::ForAnyValueArnEquals),
603            "ForAnyValue:ArnLike" => Ok(IAMOperator::ForAnyValueArnLike),
604            "ForAllValues:ArnNotEquals" => Ok(IAMOperator::ForAllValuesArnNotEquals),
605            "ForAllValues:ArnNotLike" => Ok(IAMOperator::ForAllValuesArnNotLike),
606            "ForAnyValue:ArnNotEquals" => Ok(IAMOperator::ForAnyValueArnNotEquals),
607            "ForAnyValue:ArnNotLike" => Ok(IAMOperator::ForAnyValueArnNotLike),
608            "Null" => Ok(IAMOperator::Null),
609            "StringEqualsIfExists" => Ok(IAMOperator::StringEqualsIfExists),
610            "StringNotEqualsIfExists" => Ok(IAMOperator::StringNotEqualsIfExists),
611            "StringEqualsIgnoreCaseIfExists" => Ok(IAMOperator::StringEqualsIgnoreCaseIfExists),
612            "StringNotEqualsIgnoreCaseIfExists" => {
613                Ok(IAMOperator::StringNotEqualsIgnoreCaseIfExists)
614            }
615            "StringLikeIfExists" => Ok(IAMOperator::StringLikeIfExists),
616            "StringNotLikeIfExists" => Ok(IAMOperator::StringNotLikeIfExists),
617            "NumericEqualsIfExists" => Ok(IAMOperator::NumericEqualsIfExists),
618            "NumericNotEqualsIfExists" => Ok(IAMOperator::NumericNotEqualsIfExists),
619            "NumericLessThanIfExists" => Ok(IAMOperator::NumericLessThanIfExists),
620            "NumericLessThanEqualsIfExists" => Ok(IAMOperator::NumericLessThanEqualsIfExists),
621            "NumericGreaterThanIfExists" => Ok(IAMOperator::NumericGreaterThanIfExists),
622            "NumericGreaterThanEqualsIfExists" => Ok(IAMOperator::NumericGreaterThanEqualsIfExists),
623            "DateEqualsIfExists" => Ok(IAMOperator::DateEqualsIfExists),
624            "DateNotEqualsIfExists" => Ok(IAMOperator::DateNotEqualsIfExists),
625            "DateLessThanIfExists" => Ok(IAMOperator::DateLessThanIfExists),
626            "DateLessThanEqualsIfExists" => Ok(IAMOperator::DateLessThanEqualsIfExists),
627            "DateGreaterThanIfExists" => Ok(IAMOperator::DateGreaterThanIfExists),
628            "DateGreaterThanEqualsIfExists" => Ok(IAMOperator::DateGreaterThanEqualsIfExists),
629            "BoolIfExists" => Ok(IAMOperator::BoolIfExists),
630            "BinaryEqualsIfExists" => Ok(IAMOperator::BinaryEqualsIfExists),
631            "IpAddressIfExists" => Ok(IAMOperator::IpAddressIfExists),
632            "NotIpAddressIfExists" => Ok(IAMOperator::NotIpAddressIfExists),
633            "ArnEqualsIfExists" => Ok(IAMOperator::ArnEqualsIfExists),
634            "ArnLikeIfExists" => Ok(IAMOperator::ArnLikeIfExists),
635            "ArnNotEqualsIfExists" => Ok(IAMOperator::ArnNotEqualsIfExists),
636            "ArnNotLikeIfExists" => Ok(IAMOperator::ArnNotLikeIfExists),
637            _ => Err(format!("Unknown operator: {s}")),
638        }
639    }
640}
641
642#[cfg(test)]
643mod tests {
644    use super::*;
645    use serde_json;
646
647    #[test]
648    fn test_operator_serialization() {
649        let operator = IAMOperator::StringEquals;
650        let json = serde_json::to_string(&operator).unwrap();
651        assert_eq!(json, "\"StringEquals\"");
652
653        let deserialized: IAMOperator = serde_json::from_str(&json).unwrap();
654        assert_eq!(operator, deserialized);
655    }
656
657    #[test]
658    fn test_multivalued_operators() {
659        let operator = IAMOperator::ForAllValuesStringEquals;
660        let json = serde_json::to_string(&operator).unwrap();
661        assert_eq!(json, "\"ForAllValues:StringEquals\"");
662
663        let deserialized: IAMOperator = serde_json::from_str(&json).unwrap();
664        assert_eq!(operator, deserialized);
665    }
666
667    #[test]
668    fn test_operator_categories() {
669        assert!(IAMOperator::StringEquals.is_string_operator());
670        assert!(IAMOperator::NumericEquals.is_numeric_operator());
671        assert!(IAMOperator::DateEquals.is_date_operator());
672        assert!(IAMOperator::Bool.is_boolean_operator());
673        assert!(IAMOperator::ArnEquals.is_arn_operator());
674        assert!(IAMOperator::IpAddress.is_ip_operator());
675        assert!(IAMOperator::BinaryEquals.is_binary_operator());
676    }
677
678    #[test]
679    fn test_operator_features() {
680        assert!(IAMOperator::StringLike.supports_wildcards());
681        assert!(IAMOperator::StringEquals.supports_policy_variables());
682        assert!(!IAMOperator::NumericEquals.supports_policy_variables());
683        assert!(IAMOperator::ForAllValuesStringEquals.is_multivalued_operator());
684        assert!(IAMOperator::StringEqualsIfExists.is_if_exists_operator());
685        assert!(IAMOperator::StringNotEquals.is_negated_operator());
686
687        // Test multiple values support
688        assert!(IAMOperator::StringEquals.supports_multiple_values());
689        assert!(IAMOperator::StringNotEquals.supports_multiple_values());
690        assert!(IAMOperator::NumericEquals.supports_multiple_values());
691        assert!(IAMOperator::DateEquals.supports_multiple_values());
692        assert!(IAMOperator::IpAddress.supports_multiple_values());
693        assert!(IAMOperator::ArnEquals.supports_multiple_values());
694
695        // These should not support multiple values
696        assert!(!IAMOperator::Bool.supports_multiple_values());
697        assert!(!IAMOperator::BoolIfExists.supports_multiple_values());
698        assert!(!IAMOperator::Null.supports_multiple_values());
699    }
700
701    #[test]
702    fn test_operator_category_strings() {
703        assert_eq!(IAMOperator::StringEquals.category(), OperatorType::String);
704        assert_eq!(IAMOperator::NumericEquals.category(), OperatorType::Numeric);
705        assert_eq!(IAMOperator::DateEquals.category(), OperatorType::Date);
706        assert_eq!(IAMOperator::Bool.category(), OperatorType::Boolean);
707        assert_eq!(IAMOperator::BinaryEquals.category(), OperatorType::Binary);
708        assert_eq!(IAMOperator::IpAddress.category(), OperatorType::IpAddress);
709        assert_eq!(IAMOperator::ArnEquals.category(), OperatorType::Arn);
710        assert_eq!(IAMOperator::Null.category(), OperatorType::Null);
711    }
712
713    #[test]
714    fn test_operator_string_conversion() {
715        assert_eq!(IAMOperator::StringEquals.as_str(), "StringEquals");
716        assert_eq!(
717            IAMOperator::ForAllValuesStringEquals.as_str(),
718            "ForAllValues:StringEquals"
719        );
720        assert_eq!(
721            IAMOperator::StringEqualsIfExists.as_str(),
722            "StringEqualsIfExists"
723        );
724    }
725
726    #[test]
727    fn test_operator_from_str() {
728        assert_eq!(
729            "StringEquals".parse::<IAMOperator>().unwrap(),
730            IAMOperator::StringEquals
731        );
732        assert_eq!(
733            "ForAllValues:StringEquals".parse::<IAMOperator>().unwrap(),
734            IAMOperator::ForAllValuesStringEquals
735        );
736        assert_eq!(
737            "StringEqualsIfExists".parse::<IAMOperator>().unwrap(),
738            IAMOperator::StringEqualsIfExists
739        );
740
741        assert!("InvalidOperator".parse::<IAMOperator>().is_err());
742    }
743}