Skip to main content

rs_teststand/expression/operator/
comparison.rs

1//! Comparison operators.
2
3use super::Arity;
4
5/// A comparison operator, yielding a boolean.
6///
7/// Measured behaviors worth knowing:
8///
9/// * **Comparing two strings ignores case**: `"ABC" == "abc"` is true.
10/// * A string compared with a number is converted to a number first.
11/// * Two non-zero real values compare to 14 significant digits.
12/// * `NAN` and `IND` count as equal to each other.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum ComparisonOperator {
15    /// Equality, `==`.
16    Equal,
17    /// Inequality, `!=`.
18    NotEqual,
19    /// Less than, `<`.
20    Less,
21    /// Greater than, `>`.
22    Greater,
23    /// Less than or equal, `<=`.
24    LessOrEqual,
25    /// Greater than or equal, `>=`.
26    GreaterOrEqual,
27}
28
29impl ComparisonOperator {
30    /// Every comparison operator.
31    pub const ALL: [Self; 6] = [
32        Self::Equal,
33        Self::NotEqual,
34        Self::Less,
35        Self::Greater,
36        Self::LessOrEqual,
37        Self::GreaterOrEqual,
38    ];
39
40    /// How the operator is written.
41    #[must_use]
42    pub const fn symbol(self) -> &'static str {
43        match self {
44            Self::Equal => "==",
45            Self::NotEqual => "!=",
46            Self::Less => "<",
47            Self::Greater => ">",
48            Self::LessOrEqual => "<=",
49            Self::GreaterOrEqual => ">=",
50        }
51    }
52
53    /// Always two operands.
54    #[must_use]
55    #[allow(
56        clippy::unused_self,
57        reason = "uniform surface across operator families"
58    )]
59    pub const fn arity(self) -> Arity {
60        Arity::Binary
61    }
62
63    /// Binding strength; lower binds tighter. Measured, see [`super`].
64    #[must_use]
65    #[allow(
66        clippy::unused_self,
67        reason = "uniform surface across operator families"
68    )]
69    pub const fn precedence(self) -> u8 {
70        5
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::ComparisonOperator;
77
78    #[test]
79    fn all_comparisons_share_one_binding_level() {
80        let levels: Vec<u8> = ComparisonOperator::ALL
81            .iter()
82            .map(|op| op.precedence())
83            .collect();
84        assert!(levels.windows(2).all(|pair| pair.first() == pair.last()));
85    }
86
87    #[test]
88    fn every_comparison_has_a_distinct_symbol() {
89        let mut symbols: Vec<&str> = ComparisonOperator::ALL
90            .iter()
91            .map(|op| op.symbol())
92            .collect();
93        symbols.sort_unstable();
94        let count = symbols.len();
95        symbols.dedup();
96        assert_eq!(symbols.len(), count);
97    }
98}