term_guard/constraints/
assertion.rs

1//! Assertion types for statistical constraints.
2
3use serde::{Deserialize, Serialize};
4use std::fmt;
5/// An assertion that can be evaluated against a numeric value.
6///
7/// Used by statistical constraints to define success criteria.
8///
9/// # Examples
10///
11/// ```rust
12/// use term_guard::constraints::Assertion;
13///
14/// // Value must equal 100
15/// let assertion = Assertion::Equals(100.0);
16/// assert!(assertion.evaluate(100.0));
17///
18/// // Value must be greater than 50
19/// let assertion = Assertion::GreaterThan(50.0);
20/// assert!(assertion.evaluate(75.0));
21///
22/// // Value must be between 10 and 20
23/// let assertion = Assertion::Between(10.0, 20.0);
24/// assert!(assertion.evaluate(15.0));
25/// ```
26#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
27pub enum Assertion {
28    /// Value must equal the specified value (with epsilon tolerance)
29    Equals(f64),
30    /// Value must not equal the specified value
31    NotEquals(f64),
32    /// Value must be greater than the specified value
33    GreaterThan(f64),
34    /// Value must be greater than or equal to the specified value
35    GreaterThanOrEqual(f64),
36    /// Value must be less than the specified value
37    LessThan(f64),
38    /// Value must be less than or equal to the specified value
39    LessThanOrEqual(f64),
40    /// Value must be between the specified range (inclusive)
41    Between(f64, f64),
42    /// Value must not be between the specified range
43    NotBetween(f64, f64),
44}
45
46impl Assertion {
47    /// Evaluates the assertion against a value.
48    pub fn evaluate(&self, value: f64) -> bool {
49        const EPSILON: f64 = 1e-10;
50
51        match self {
52            Assertion::Equals(expected) => (value - expected).abs() < EPSILON,
53            Assertion::NotEquals(expected) => (value - expected).abs() >= EPSILON,
54            Assertion::GreaterThan(threshold) => value > *threshold,
55            Assertion::GreaterThanOrEqual(threshold) => value >= *threshold,
56            Assertion::LessThan(threshold) => value < *threshold,
57            Assertion::LessThanOrEqual(threshold) => value <= *threshold,
58            Assertion::Between(min, max) => value >= *min && value <= *max,
59            Assertion::NotBetween(min, max) => value < *min || value > *max,
60        }
61    }
62
63    /// Returns a human-readable description of the assertion.
64    pub fn description(&self) -> String {
65        match self {
66            Assertion::Equals(v) => format!("equals {v}"),
67            Assertion::NotEquals(v) => format!("not equals {v}"),
68            Assertion::GreaterThan(v) => format!("greater than {v}"),
69            Assertion::GreaterThanOrEqual(v) => format!("greater than or equal to {v}"),
70            Assertion::LessThan(v) => format!("less than {v}"),
71            Assertion::LessThanOrEqual(v) => format!("less than or equal to {v}"),
72            Assertion::Between(min, max) => format!("between {min} and {max}"),
73            Assertion::NotBetween(min, max) => format!("not between {min} and {max}"),
74        }
75    }
76}
77
78impl fmt::Display for Assertion {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        write!(f, "{}", self.description())
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn test_equals() {
90        let assertion = Assertion::Equals(10.0);
91        assert!(assertion.evaluate(10.0));
92        assert!(!assertion.evaluate(10.1));
93    }
94
95    #[test]
96    fn test_not_equals() {
97        let assertion = Assertion::NotEquals(10.0);
98        assert!(!assertion.evaluate(10.0));
99        assert!(assertion.evaluate(10.1));
100    }
101
102    #[test]
103    fn test_greater_than() {
104        let assertion = Assertion::GreaterThan(10.0);
105        assert!(assertion.evaluate(10.1));
106        assert!(!assertion.evaluate(10.0));
107        assert!(!assertion.evaluate(9.9));
108    }
109
110    #[test]
111    fn test_between() {
112        let assertion = Assertion::Between(10.0, 20.0);
113        assert!(assertion.evaluate(15.0));
114        assert!(assertion.evaluate(10.0));
115        assert!(assertion.evaluate(20.0));
116        assert!(!assertion.evaluate(9.9));
117        assert!(!assertion.evaluate(20.1));
118    }
119
120    #[test]
121    fn test_description() {
122        assert_eq!(Assertion::Equals(10.0).description(), "equals 10");
123        assert_eq!(Assertion::GreaterThan(5.0).description(), "greater than 5");
124        assert_eq!(
125            Assertion::Between(1.0, 10.0).description(),
126            "between 1 and 10"
127        );
128    }
129}