scratchstack_aspen/condition/
op.rs

1use {
2    super::{
3        arn::{arn_match, ArnCmp},
4        binary::{binary_match, BINARY_DISPLAY_NAMES},
5        boolean::{bool_match, BOOL_DISPLAY_NAMES},
6        date::{date_match, DateCmp},
7        ipaddr::{ip_address_match, IP_ADDRESS_DISPLAY_NAMES},
8        null::{null_match, NULL_DISPLAY_NAME},
9        numeric::{numeric_match, NumericCmp},
10        string::{string_match, StringCmp},
11        variant::Variant,
12    },
13    crate::{serutil::StringLikeList, AspenError, Context, PolicyVersion},
14    scratchstack_aws_principal::SessionValue,
15    serde::{de, de::Deserializer, ser::Serializer, Deserialize, Serialize},
16    std::{
17        borrow::Borrow,
18        collections::BTreeMap,
19        fmt::{Debug, Display, Formatter, Result as FmtResult},
20        str::FromStr,
21    },
22};
23
24/// An operator for a condition clause.
25#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
26pub enum ConditionOp {
27    /// Operators for ARNs.
28    Arn(ArnCmp, Variant),
29
30    /// Operators for binary values. Variant here is only allowed to be [Variant::None] or [Variant::IfExists].
31    Binary(Variant),
32
33    /// Operators on boolean values. Variant here is only allowed to be [Variant::None] or [Variant::IfExists].
34    Bool(Variant),
35
36    /// Operators for date/time values.
37    Date(DateCmp, Variant),
38
39    /// Operators on IP addresses and networks.
40    IpAddress(Variant),
41
42    /// Operator on the presence/absence of a value.
43    Null,
44
45    /// Operators on numeric values.
46    Numeric(NumericCmp, Variant),
47
48    /// Operators on string vaules.
49    String(StringCmp, Variant),
50}
51
52/// The `ArnEquals` operator.
53pub const ArnEquals: ConditionOp = ConditionOp::Arn(ArnCmp::Equals, Variant::None);
54
55/// The `ArnEqualsIfExists` operator.
56pub const ArnEqualsIfExists: ConditionOp = ConditionOp::Arn(ArnCmp::Equals, Variant::IfExists);
57
58/// The `ArnNotEquals` operator.
59pub const ArnNotEquals: ConditionOp = ConditionOp::Arn(ArnCmp::Equals, Variant::Negated);
60
61/// The `ArnNotEqualsIfExists` operator.
62pub const ArnNotEqualsIfExists: ConditionOp = ConditionOp::Arn(ArnCmp::Equals, Variant::IfExistsNegated);
63
64/// The `ArnLike` operator.
65pub const ArnLike: ConditionOp = ConditionOp::Arn(ArnCmp::Like, Variant::None);
66
67/// The `ArnLikeIfExists` operator.
68pub const ArnLikeIfExists: ConditionOp = ConditionOp::Arn(ArnCmp::Like, Variant::IfExists);
69
70/// The `ArnNotLike` operator.
71pub const ArnNotLike: ConditionOp = ConditionOp::Arn(ArnCmp::Like, Variant::Negated);
72
73/// The `ArnNotLikeIfExists` operator.
74pub const ArnNotLikeIfExists: ConditionOp = ConditionOp::Arn(ArnCmp::Like, Variant::IfExistsNegated);
75
76/// The `BinaryEquals` operator.
77pub const BinaryEquals: ConditionOp = ConditionOp::Binary(Variant::None);
78
79/// The `BinaryEqualsIfExists` operator.
80pub const BinaryEqualsIfExists: ConditionOp = ConditionOp::Binary(Variant::IfExists);
81
82/// The `BinaryNotEquals` operator.
83pub const Bool: ConditionOp = ConditionOp::Bool(Variant::None);
84
85/// The `BoolIfExists` operator.
86pub const BoolIfExists: ConditionOp = ConditionOp::Bool(Variant::IfExists);
87
88/// The `DateEquals` operator.
89pub const DateEquals: ConditionOp = ConditionOp::Date(DateCmp::Equals, Variant::None);
90
91/// The `DateEqualsIfExists` operator.
92pub const DateEqualsIfExists: ConditionOp = ConditionOp::Date(DateCmp::Equals, Variant::IfExists);
93
94/// The `DateNotEquals` operator.
95pub const DateNotEquals: ConditionOp = ConditionOp::Date(DateCmp::Equals, Variant::Negated);
96
97/// The `DateNotEqualsIfExists` operator.
98pub const DateNotEqualsIfExists: ConditionOp = ConditionOp::Date(DateCmp::Equals, Variant::IfExistsNegated);
99
100/// The `DateLessThan` operator.
101pub const DateLessThan: ConditionOp = ConditionOp::Date(DateCmp::LessThan, Variant::None);
102
103/// The `DateLessThanIfExists` operator.
104pub const DateLessThanIfExists: ConditionOp = ConditionOp::Date(DateCmp::LessThan, Variant::IfExists);
105
106/// The `DateGreaterThanEquals` operator.
107pub const DateGreaterThanEquals: ConditionOp = ConditionOp::Date(DateCmp::LessThan, Variant::Negated);
108
109/// The `DateGreaterThanEqualsIfExists` operator.
110pub const DateGreaterThanEqualsIfExists: ConditionOp = ConditionOp::Date(DateCmp::LessThan, Variant::IfExistsNegated);
111
112/// The `DateLessThanEquals` operator.
113pub const DateLessThanEquals: ConditionOp = ConditionOp::Date(DateCmp::LessThanEquals, Variant::None);
114
115/// The `DateLessThanEqualsIfExists` operator.
116pub const DateLessThanEqualsIfExists: ConditionOp = ConditionOp::Date(DateCmp::LessThanEquals, Variant::IfExists);
117
118/// The `DateGreaterThan` operator.
119pub const DateGreaterThan: ConditionOp = ConditionOp::Date(DateCmp::LessThanEquals, Variant::Negated);
120
121/// The `DateGreaterThanIfExists` operator.
122pub const DateGreaterThanIfExists: ConditionOp = ConditionOp::Date(DateCmp::LessThanEquals, Variant::IfExistsNegated);
123
124/// The `IpAddress` operator.
125pub const IpAddress: ConditionOp = ConditionOp::IpAddress(Variant::None);
126
127/// The `IpAddressIfExists` operator.
128pub const IpAddressIfExists: ConditionOp = ConditionOp::IpAddress(Variant::IfExists);
129
130/// The `NotIpAddress` operator.
131pub const NotIpAddress: ConditionOp = ConditionOp::IpAddress(Variant::Negated);
132
133/// The `NotIpAddressIfExists` operator.
134pub const NotIpAddressIfExists: ConditionOp = ConditionOp::IpAddress(Variant::IfExistsNegated);
135
136/// The `Null` operator.
137pub const Null: ConditionOp = ConditionOp::Null;
138
139/// The `NumericEquals` operator.
140pub const NumericEquals: ConditionOp = ConditionOp::Numeric(NumericCmp::Equals, Variant::None);
141
142/// The `NumericEqualsIfExists` operator.
143pub const NumericEqualsIfExists: ConditionOp = ConditionOp::Numeric(NumericCmp::Equals, Variant::IfExists);
144
145/// The `NumericNotEquals` operator.
146pub const NumericNotEquals: ConditionOp = ConditionOp::Numeric(NumericCmp::Equals, Variant::Negated);
147
148/// The `NumericNotEqualsIfExists` operator.
149pub const NumericNotEqualsIfExists: ConditionOp = ConditionOp::Numeric(NumericCmp::Equals, Variant::IfExistsNegated);
150
151/// The `NumericLessThan` operator.
152pub const NumericLessThan: ConditionOp = ConditionOp::Numeric(NumericCmp::LessThan, Variant::None);
153
154/// The `NumericLessThanIfExists` operator.
155pub const NumericLessThanIfExists: ConditionOp = ConditionOp::Numeric(NumericCmp::LessThan, Variant::IfExists);
156
157/// The `NumericGreaterThanEquals` operator.
158pub const NumericGreaterThanEquals: ConditionOp = ConditionOp::Numeric(NumericCmp::LessThan, Variant::Negated);
159
160/// The `NumericGreaterThanEqualsIfExists` operator.
161pub const NumericGreaterThanEqualsIfExists: ConditionOp =
162    ConditionOp::Numeric(NumericCmp::LessThan, Variant::IfExistsNegated);
163
164/// The `NumericLessThanEquals` operator.
165pub const NumericLessThanEquals: ConditionOp = ConditionOp::Numeric(NumericCmp::LessThanEquals, Variant::None);
166
167/// The `NumericLessThanEqualsIfExists` operator.
168pub const NumericLessThanEqualsIfExists: ConditionOp =
169    ConditionOp::Numeric(NumericCmp::LessThanEquals, Variant::IfExists);
170
171/// The `NumericGreaterThan` operator.
172pub const NumericGreaterThan: ConditionOp = ConditionOp::Numeric(NumericCmp::LessThanEquals, Variant::Negated);
173
174/// The `NumericGreaterThanIfExists` operator.
175pub const NumericGreaterThanIfExists: ConditionOp =
176    ConditionOp::Numeric(NumericCmp::LessThanEquals, Variant::IfExistsNegated);
177
178/// The `StringEquals` operator.
179pub const StringEquals: ConditionOp = ConditionOp::String(StringCmp::Equals, Variant::None);
180
181/// The `StringEqualsIfExists` operator.
182pub const StringEqualsIfExists: ConditionOp = ConditionOp::String(StringCmp::Equals, Variant::IfExists);
183
184/// The `StringNotEquals` operator.
185pub const StringNotEquals: ConditionOp = ConditionOp::String(StringCmp::Equals, Variant::Negated);
186
187/// The `StringNotEqualsIfExists` operator.
188pub const StringNotEqualsIfExists: ConditionOp = ConditionOp::String(StringCmp::Equals, Variant::IfExistsNegated);
189
190/// The `StringEqualsIgnoreCase` operator.
191pub const StringEqualsIgnoreCase: ConditionOp = ConditionOp::String(StringCmp::EqualsIgnoreCase, Variant::None);
192
193/// The `StringEqualsIgnoreCaseIfExists` operator.
194pub const StringEqualsIgnoreCaseIfExists: ConditionOp =
195    ConditionOp::String(StringCmp::EqualsIgnoreCase, Variant::IfExists);
196
197/// The `StringNotEqualsIgnoreCase` operator.
198pub const StringNotEqualsIgnoreCase: ConditionOp = ConditionOp::String(StringCmp::EqualsIgnoreCase, Variant::Negated);
199
200/// The `StringNotEqualsIgnoreCaseIfExists` operator.
201pub const StringNotEqualsIgnoreCaseIfExists: ConditionOp =
202    ConditionOp::String(StringCmp::EqualsIgnoreCase, Variant::IfExistsNegated);
203
204/// The `StringLike` operator.
205pub const StringLike: ConditionOp = ConditionOp::String(StringCmp::Like, Variant::None);
206
207/// The `StringLikeIfExists` operator.
208pub const StringLikeIfExists: ConditionOp = ConditionOp::String(StringCmp::Like, Variant::IfExists);
209
210/// The `StringNotLike` operator.
211pub const StringNotLike: ConditionOp = ConditionOp::String(StringCmp::Like, Variant::Negated);
212
213/// The `StringNotLikeIfExists` operator.
214pub const StringNotLikeIfExists: ConditionOp = ConditionOp::String(StringCmp::Like, Variant::IfExistsNegated);
215
216impl Borrow<str> for ConditionOp {
217    fn borrow(&self) -> &str {
218        match self {
219            Self::Arn(cmp, variant) => cmp.display_name(variant),
220            Self::Binary(variant) => BINARY_DISPLAY_NAMES[variant.as_usize()],
221            Self::Bool(variant) => BOOL_DISPLAY_NAMES[variant.as_usize()],
222            Self::Date(cmp, variant) => cmp.display_name(variant),
223            Self::IpAddress(variant) => IP_ADDRESS_DISPLAY_NAMES[variant.as_usize()],
224            Self::Null => NULL_DISPLAY_NAME,
225            Self::Numeric(cmp, variant) => cmp.display_name(variant),
226            Self::String(cmp, variant) => cmp.display_name(variant),
227        }
228    }
229}
230
231impl PartialEq<str> for ConditionOp {
232    fn eq(&self, other: &str) -> bool {
233        self.to_string().as_str() == other
234    }
235}
236
237impl Display for ConditionOp {
238    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
239        f.write_str(self.borrow())
240    }
241}
242
243impl<'de> Deserialize<'de> for ConditionOp {
244    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
245        let s = String::deserialize(deserializer)?;
246        ConditionOp::from_str(&s).map_err(de::Error::custom)
247    }
248}
249
250impl Serialize for ConditionOp {
251    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
252        serializer.serialize_str(&self.to_string())
253    }
254}
255
256const NULL: SessionValue = SessionValue::Null;
257
258impl ConditionOp {
259    /// Indicates whether this condition operator matches the request [Context].
260    ///
261    /// Any variables in the condition are resolved according to the specified [PolicyVersion].
262    ///
263    /// # Errors
264    ///
265    /// If a condition clause contains a malformed variable reference and [PolicyVersion::V2012_10_17] or later is
266    /// used, [AspenError::InvalidSubstitution] is returned.
267    pub fn matches(
268        &self,
269        condition: &BTreeMap<String, StringLikeList<String>>,
270        context: &Context,
271        pv: PolicyVersion,
272    ) -> Result<bool, AspenError> {
273        for (key, allowed) in condition.iter() {
274            let value = context.session_data().get(key).unwrap_or(&NULL);
275
276            let result = match self {
277                Self::Arn(cmp, variant) => arn_match(context, pv, allowed, value, *cmp, *variant),
278                Self::Binary(variant) => binary_match(context, pv, allowed, value, *variant),
279                Self::Bool(variant) => bool_match(context, pv, allowed, value, *variant),
280                Self::Date(cmp, variant) => date_match(context, pv, allowed, value, *cmp, *variant),
281                Self::IpAddress(variant) => ip_address_match(context, pv, allowed, value, *variant),
282                Self::Null => null_match(context, pv, allowed, value),
283                Self::Numeric(cmp, variant) => numeric_match(context, pv, allowed, value, *cmp, *variant),
284                Self::String(cmp, variant) => string_match(context, pv, allowed, value, *cmp, *variant),
285            }?;
286
287            if !result {
288                return Ok(false);
289            }
290        }
291
292        Ok(true)
293    }
294}
295
296impl FromStr for ConditionOp {
297    type Err = AspenError;
298
299    fn from_str(s: &str) -> Result<Self, Self::Err> {
300        match s {
301            "ArnEquals" => Ok(ArnEquals),
302            "ArnEqualsIfExists" => Ok(ArnEqualsIfExists),
303            "ArnNotEquals" => Ok(ArnNotEquals),
304            "ArnNotEqualsIfExists" => Ok(ArnNotEqualsIfExists),
305            "ArnLike" => Ok(ArnLike),
306            "ArnLikeIfExists" => Ok(ArnLikeIfExists),
307            "ArnNotLike" => Ok(ArnNotLike),
308            "ArnNotLikeIfExists" => Ok(ArnNotLikeIfExists),
309            "BinaryEquals" => Ok(BinaryEquals),
310            "BinaryEqualsIfExists" => Ok(BinaryEqualsIfExists),
311            "Bool" => Ok(Bool),
312            "BoolIfExists" => Ok(BoolIfExists),
313            "DateEquals" => Ok(DateEquals),
314            "DateEqualsIfExists" => Ok(DateEqualsIfExists),
315            "DateNotEquals" => Ok(DateNotEquals),
316            "DateNotEqualsIfExists" => Ok(DateNotEqualsIfExists),
317            "DateLessThan" => Ok(DateLessThan),
318            "DateLessThanIfExists" => Ok(DateLessThanIfExists),
319            "DateGreaterThanEquals" => Ok(DateGreaterThanEquals),
320            "DateGreaterThanEqualsIfExists" => Ok(DateGreaterThanEqualsIfExists),
321            "DateLessThanEquals" => Ok(DateLessThanEquals),
322            "DateLessThanEqualsIfExists" => Ok(DateLessThanEqualsIfExists),
323            "DateGreaterThan" => Ok(DateGreaterThan),
324            "DateGreaterThanIfExists" => Ok(DateGreaterThanIfExists),
325            "IpAddress" => Ok(IpAddress),
326            "IpAddressIfExists" => Ok(IpAddressIfExists),
327            "NotIpAddress" => Ok(NotIpAddress),
328            "NotIpAddressIfExists" => Ok(NotIpAddressIfExists),
329            "Null" => Ok(Null),
330            "NumericEquals" => Ok(NumericEquals),
331            "NumericEqualsIfExists" => Ok(NumericEqualsIfExists),
332            "NumericNotEquals" => Ok(NumericNotEquals),
333            "NumericNotEqualsIfExists" => Ok(NumericNotEqualsIfExists),
334            "NumericLessThan" => Ok(NumericLessThan),
335            "NumericLessThanIfExists" => Ok(NumericLessThanIfExists),
336            "NumericGreaterThanEquals" => Ok(NumericGreaterThanEquals),
337            "NumericGreaterThanEqualsIfExists" => Ok(NumericGreaterThanEqualsIfExists),
338            "NumericLessThanEquals" => Ok(NumericLessThanEquals),
339            "NumericLessThanEqualsIfExists" => Ok(NumericLessThanEqualsIfExists),
340            "NumericGreaterThan" => Ok(NumericGreaterThan),
341            "NumericGreaterThanIfExists" => Ok(NumericGreaterThanIfExists),
342            "StringEquals" => Ok(StringEquals),
343            "StringEqualsIfExists" => Ok(StringEqualsIfExists),
344            "StringNotEquals" => Ok(StringNotEquals),
345            "StringNotEqualsIfExists" => Ok(StringNotEqualsIfExists),
346            "StringEqualsIgnoreCase" => Ok(StringEqualsIgnoreCase),
347            "StringEqualsIgnoreCaseIfExists" => Ok(StringEqualsIgnoreCaseIfExists),
348            "StringNotEqualsIgnoreCase" => Ok(StringNotEqualsIgnoreCase),
349            "StringNotEqualsIgnoreCaseIfExists" => Ok(StringNotEqualsIgnoreCaseIfExists),
350            "StringLike" => Ok(StringLike),
351            "StringLikeIfExists" => Ok(StringLikeIfExists),
352            "StringNotLike" => Ok(StringNotLike),
353            "StringNotLikeIfExists" => Ok(StringNotLikeIfExists),
354            _ => Err(AspenError::InvalidConditionOperator(s.to_string())),
355        }
356    }
357}
358
359#[cfg(test)]
360mod tests {
361    use {
362        crate::{
363            condition::{
364                arn::ArnCmp, date::DateCmp, numeric::NumericCmp, op::ConditionOp, string::StringCmp, variant::Variant,
365            },
366            condop,
367        },
368        std::{
369            cmp::{Ordering, PartialOrd},
370            collections::hash_map::DefaultHasher,
371            hash::{Hash, Hasher},
372            str::FromStr,
373        },
374    };
375
376    #[test_log::test]
377    fn test_derived() {
378        let cops = vec![
379            (ConditionOp::Arn(ArnCmp::Equals, Variant::None), "Arn(Equals, None)"),
380            (ConditionOp::Arn(ArnCmp::Equals, Variant::IfExists), "Arn(Equals, IfExists)"),
381            (ConditionOp::Arn(ArnCmp::Equals, Variant::Negated), "Arn(Equals, Negated)"),
382            (ConditionOp::Arn(ArnCmp::Equals, Variant::IfExistsNegated), "Arn(Equals, IfExistsNegated)"),
383            (ConditionOp::Arn(ArnCmp::Like, Variant::None), "Arn(Like, None)"),
384            (ConditionOp::Arn(ArnCmp::Like, Variant::IfExists), "Arn(Like, IfExists)"),
385            (ConditionOp::Arn(ArnCmp::Like, Variant::Negated), "Arn(Like, Negated)"),
386            (ConditionOp::Arn(ArnCmp::Like, Variant::IfExistsNegated), "Arn(Like, IfExistsNegated)"),
387            (ConditionOp::Binary(Variant::None), "Binary(None)"),
388            (ConditionOp::Binary(Variant::IfExists), "Binary(IfExists)"),
389            (ConditionOp::Bool(Variant::None), "Bool(None)"),
390            (ConditionOp::Bool(Variant::IfExists), "Bool(IfExists)"),
391            (ConditionOp::Date(DateCmp::Equals, Variant::None), "Date(Equals, None)"),
392            (ConditionOp::Date(DateCmp::Equals, Variant::IfExists), "Date(Equals, IfExists)"),
393            (ConditionOp::Date(DateCmp::Equals, Variant::Negated), "Date(Equals, Negated)"),
394            (ConditionOp::Date(DateCmp::Equals, Variant::IfExistsNegated), "Date(Equals, IfExistsNegated)"),
395            (ConditionOp::Date(DateCmp::LessThan, Variant::None), "Date(LessThan, None)"),
396            (ConditionOp::Date(DateCmp::LessThan, Variant::IfExists), "Date(LessThan, IfExists)"),
397            (ConditionOp::Date(DateCmp::LessThan, Variant::Negated), "Date(LessThan, Negated)"),
398            (ConditionOp::Date(DateCmp::LessThan, Variant::IfExistsNegated), "Date(LessThan, IfExistsNegated)"),
399            (ConditionOp::Date(DateCmp::LessThanEquals, Variant::None), "Date(LessThanEquals, None)"),
400            (ConditionOp::Date(DateCmp::LessThanEquals, Variant::IfExists), "Date(LessThanEquals, IfExists)"),
401            (ConditionOp::Date(DateCmp::LessThanEquals, Variant::Negated), "Date(LessThanEquals, Negated)"),
402            (
403                ConditionOp::Date(DateCmp::LessThanEquals, Variant::IfExistsNegated),
404                "Date(LessThanEquals, IfExistsNegated)",
405            ),
406            (ConditionOp::IpAddress(Variant::None), "IpAddress(None)"),
407            (ConditionOp::IpAddress(Variant::IfExists), "IpAddress(IfExists)"),
408            (ConditionOp::IpAddress(Variant::Negated), "IpAddress(Negated)"),
409            (ConditionOp::IpAddress(Variant::IfExistsNegated), "IpAddress(IfExistsNegated)"),
410            (ConditionOp::Null, "Null"),
411            (ConditionOp::Numeric(NumericCmp::Equals, Variant::None), "Numeric(Equals, None)"),
412            (ConditionOp::Numeric(NumericCmp::Equals, Variant::IfExists), "Numeric(Equals, IfExists)"),
413            (ConditionOp::Numeric(NumericCmp::Equals, Variant::Negated), "Numeric(Equals, Negated)"),
414            (ConditionOp::Numeric(NumericCmp::Equals, Variant::IfExistsNegated), "Numeric(Equals, IfExistsNegated)"),
415            (ConditionOp::Numeric(NumericCmp::LessThan, Variant::None), "Numeric(LessThan, None)"),
416            (ConditionOp::Numeric(NumericCmp::LessThan, Variant::IfExists), "Numeric(LessThan, IfExists)"),
417            (ConditionOp::Numeric(NumericCmp::LessThan, Variant::Negated), "Numeric(LessThan, Negated)"),
418            (
419                ConditionOp::Numeric(NumericCmp::LessThan, Variant::IfExistsNegated),
420                "Numeric(LessThan, IfExistsNegated)",
421            ),
422            (ConditionOp::Numeric(NumericCmp::LessThanEquals, Variant::None), "Numeric(LessThanEquals, None)"),
423            (ConditionOp::Numeric(NumericCmp::LessThanEquals, Variant::IfExists), "Numeric(LessThanEquals, IfExists)"),
424            (ConditionOp::Numeric(NumericCmp::LessThanEquals, Variant::Negated), "Numeric(LessThanEquals, Negated)"),
425            (
426                ConditionOp::Numeric(NumericCmp::LessThanEquals, Variant::IfExistsNegated),
427                "Numeric(LessThanEquals, IfExistsNegated)",
428            ),
429            (ConditionOp::String(StringCmp::Equals, Variant::None), "String(Equals, None)"),
430            (ConditionOp::String(StringCmp::Equals, Variant::IfExists), "String(Equals, IfExists)"),
431            (ConditionOp::String(StringCmp::Equals, Variant::Negated), "String(Equals, Negated)"),
432            (ConditionOp::String(StringCmp::Equals, Variant::IfExistsNegated), "String(Equals, IfExistsNegated)"),
433            (ConditionOp::String(StringCmp::EqualsIgnoreCase, Variant::None), "String(EqualsIgnoreCase, None)"),
434            (ConditionOp::String(StringCmp::EqualsIgnoreCase, Variant::IfExists), "String(EqualsIgnoreCase, IfExists)"),
435            (ConditionOp::String(StringCmp::EqualsIgnoreCase, Variant::Negated), "String(EqualsIgnoreCase, Negated)"),
436            (
437                ConditionOp::String(StringCmp::EqualsIgnoreCase, Variant::IfExistsNegated),
438                "String(EqualsIgnoreCase, IfExistsNegated)",
439            ),
440            (ConditionOp::String(StringCmp::Like, Variant::None), "String(Like, None)"),
441            (ConditionOp::String(StringCmp::Like, Variant::IfExists), "String(Like, IfExists)"),
442            (ConditionOp::String(StringCmp::Like, Variant::Negated), "String(Like, Negated)"),
443            (ConditionOp::String(StringCmp::Like, Variant::IfExistsNegated), "String(Like, IfExistsNegated)"),
444        ];
445
446        for (cop, debug) in &cops {
447            assert_eq!(&format!("{cop:?}"), debug);
448        }
449
450        for i in 0..cops.len() {
451            let mut hasher = DefaultHasher::new();
452            cops[i].0.hash(&mut hasher);
453            let i_hash = hasher.finish();
454
455            for j in 0..cops.len() {
456                let mut hasher = DefaultHasher::new();
457                cops[j].0.hash(&mut hasher);
458                let j_hash = hasher.finish();
459
460                match i.cmp(&j) {
461                    Ordering::Equal => {
462                        assert_eq!(cops[i].0, cops[j].0);
463                        assert_eq!(i_hash, j_hash);
464                        assert_eq!(cops[i].0.cmp(&cops[j].0), Ordering::Equal);
465                        assert_eq!(cops[i].0.partial_cmp(&cops[j].0), Some(Ordering::Equal));
466                    }
467                    Ordering::Less => {
468                        assert_ne!(cops[i].0, cops[j].0);
469                        assert_ne!(i_hash, j_hash);
470                        assert_eq!(cops[i].0.cmp(&cops[j].0), Ordering::Less);
471                        assert_eq!(cops[i].0.partial_cmp(&cops[j].0), Some(Ordering::Less));
472                    }
473                    Ordering::Greater => {
474                        assert_ne!(cops[i].0, cops[j].0);
475                        assert_ne!(i_hash, j_hash);
476                        assert_eq!(cops[i].0.cmp(&cops[j].0), Ordering::Greater);
477                        assert_eq!(cops[i].0.partial_cmp(&cops[j].0), Some(Ordering::Greater));
478                    }
479                }
480            }
481        }
482    }
483
484    #[test_log::test]
485    fn test_deserialize_bad_type() {
486        let e = serde_json::from_str::<ConditionOp>("3").unwrap_err();
487        assert_eq!(e.to_string(), "invalid type: integer `3`, expected a string at line 1 column 1");
488
489        let c = serde_json::from_str::<ConditionOp>("\"ArnEquals\"").unwrap();
490        assert_eq!(c, condop::ArnEquals);
491    }
492
493    #[test_log::test]
494    fn test_display() {
495        let items = vec![
496            "ArnEquals",
497            "ArnEqualsIfExists",
498            "ArnLike",
499            "ArnLikeIfExists",
500            "ArnNotEquals",
501            "ArnNotEqualsIfExists",
502            "ArnNotLike",
503            "ArnNotLikeIfExists",
504            "BinaryEquals",
505            "BinaryEqualsIfExists",
506            "Bool",
507            "BoolIfExists",
508            "DateEquals",
509            "DateEqualsIfExists",
510            "DateGreaterThan",
511            "DateGreaterThanEquals",
512            "DateGreaterThanEqualsIfExists",
513            "DateGreaterThanIfExists",
514            "DateLessThan",
515            "DateLessThanEquals",
516            "DateLessThanEqualsIfExists",
517            "DateLessThanIfExists",
518            "DateNotEquals",
519            "DateNotEqualsIfExists",
520            "IpAddress",
521            "IpAddressIfExists",
522            "NotIpAddress",
523            "NotIpAddressIfExists",
524            "Null",
525            "NumericEquals",
526            "NumericEqualsIfExists",
527            "NumericGreaterThan",
528            "NumericGreaterThanEquals",
529            "NumericGreaterThanEqualsIfExists",
530            "NumericGreaterThanIfExists",
531            "NumericLessThan",
532            "NumericLessThanEquals",
533            "NumericLessThanEqualsIfExists",
534            "NumericLessThanIfExists",
535            "NumericNotEquals",
536            "NumericNotEqualsIfExists",
537            "StringEquals",
538            "StringEqualsIfExists",
539            "StringEqualsIgnoreCase",
540            "StringEqualsIgnoreCaseIfExists",
541            "StringLike",
542            "StringLikeIfExists",
543            "StringNotEquals",
544            "StringNotEqualsIfExists",
545            "StringNotEqualsIgnoreCase",
546            "StringNotEqualsIgnoreCaseIfExists",
547            "StringNotLike",
548            "StringNotLikeIfExists",
549        ];
550
551        for item in items {
552            let op = ConditionOp::from_str(item).unwrap();
553            assert_eq!(format!("{op}"), item);
554            assert_eq!(&op, item);
555        }
556    }
557}