Skip to main content

rs_teststand/expression/operator/
assignment.rs

1//! Assignment operators.
2
3use super::Arity;
4
5/// An assignment operator.
6///
7/// The plain form evaluates the right-hand side and stores it in the left-hand
8/// operand, converting between types where it can, a number into a string
9/// property, for instance. Assigning one container to another requires the
10/// subproperty names on both sides to line up.
11///
12/// The compound forms apply their operation and then assign.
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
14pub enum AssignmentOperator {
15    /// Assignment, `=`.
16    Assign,
17    /// Add and assign, `+=`.
18    Add,
19    /// Subtract and assign, `-=`.
20    Subtract,
21    /// Multiply and assign, `*=`.
22    Multiply,
23    /// Divide and assign, `/=`.
24    Divide,
25    /// Take the remainder and assign, `%=`.
26    Modulo,
27    /// Exclusive-or and assign, `^=`.
28    Xor,
29    /// Bitwise-or and assign, `|=`.
30    Or,
31    /// Bitwise-and and assign, `&=`.
32    And,
33}
34
35impl AssignmentOperator {
36    /// Every assignment operator.
37    pub const ALL: [Self; 9] = [
38        Self::Assign,
39        Self::Add,
40        Self::Subtract,
41        Self::Multiply,
42        Self::Divide,
43        Self::Modulo,
44        Self::Xor,
45        Self::Or,
46        Self::And,
47    ];
48
49    /// How the operator is written.
50    #[must_use]
51    pub const fn symbol(self) -> &'static str {
52        match self {
53            Self::Assign => "=",
54            Self::Add => "+=",
55            Self::Subtract => "-=",
56            Self::Multiply => "*=",
57            Self::Divide => "/=",
58            Self::Modulo => "%=",
59            Self::Xor => "^=",
60            Self::Or => "|=",
61            Self::And => "&=",
62        }
63    }
64
65    /// Always two operands.
66    #[must_use]
67    #[allow(
68        clippy::unused_self,
69        reason = "uniform surface across operator families"
70    )]
71    pub const fn arity(self) -> Arity {
72        Arity::Binary
73    }
74
75    /// Binding strength, deliberately unmeasured.
76    ///
77    /// Assignment binds loosest of everything here, but the exact level was not
78    /// established by evaluation, and a number that has not been verified would
79    /// be a guess presented as a fact. `None` says so honestly.
80    #[must_use]
81    #[allow(
82        clippy::unused_self,
83        reason = "uniform surface across operator families"
84    )]
85    pub const fn precedence(self) -> Option<u8> {
86        None
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::AssignmentOperator;
93
94    #[test]
95    fn precedence_is_reported_as_unknown_rather_than_invented() {
96        for operator in AssignmentOperator::ALL {
97            assert_eq!(operator.precedence(), None, "{operator:?}");
98        }
99    }
100
101    #[test]
102    fn every_compound_form_ends_in_the_assignment_symbol() {
103        for operator in AssignmentOperator::ALL {
104            assert!(operator.symbol().ends_with('='), "{operator:?}");
105        }
106    }
107
108    #[test]
109    fn every_assignment_has_a_distinct_symbol() {
110        let mut symbols: Vec<&str> = AssignmentOperator::ALL
111            .iter()
112            .map(|op| op.symbol())
113            .collect();
114        symbols.sort_unstable();
115        let count = symbols.len();
116        symbols.dedup();
117        assert_eq!(symbols.len(), count);
118    }
119}