asserting/range/
mod.rs

1//! Implementation of assertions for `Range` and `RangeInclusive` values.
2
3use crate::assertions::AssertInRange;
4use crate::colored::{mark_missing, mark_missing_string, mark_unexpected};
5use crate::expectations::{is_in_range, not, IsInRange};
6use crate::properties::IsEmptyProperty;
7use crate::spec::{DiffFormat, Expectation, Expression, FailingStrategy, Invertible, Spec};
8use crate::std::fmt::Debug;
9use crate::std::format;
10use crate::std::ops::{Bound, Range, RangeBounds, RangeInclusive};
11use crate::std::string::String;
12
13impl<T> IsEmptyProperty for Range<T>
14where
15    T: PartialEq,
16{
17    fn is_empty_property(&self) -> bool {
18        self.start == self.end
19    }
20}
21
22impl<T> IsEmptyProperty for RangeInclusive<T>
23where
24    T: PartialOrd,
25{
26    fn is_empty_property(&self) -> bool {
27        self.start() > self.end()
28    }
29}
30
31impl<S, E, R> AssertInRange<E> for Spec<'_, S, R>
32where
33    S: PartialOrd<E> + Debug,
34    E: PartialOrd<S> + Debug,
35    R: FailingStrategy,
36{
37    fn is_in_range<U>(self, range: U) -> Self
38    where
39        U: RangeBounds<E> + Debug,
40    {
41        self.expecting(is_in_range(range))
42    }
43
44    fn is_not_in_range<U>(self, range: U) -> Self
45    where
46        U: RangeBounds<E> + Debug,
47    {
48        self.expecting(not(is_in_range(range)))
49    }
50}
51
52impl<S, E, R> Expectation<S> for IsInRange<R, E>
53where
54    S: PartialOrd<E> + Debug,
55    E: PartialOrd<S> + Debug,
56    R: RangeBounds<E> + Debug,
57{
58    fn test(&mut self, subject: &S) -> bool {
59        self.expected_range.contains(subject)
60    }
61
62    fn message(
63        &self,
64        expression: &Expression<'_>,
65        actual: &S,
66        inverted: bool,
67        format: &DiffFormat,
68    ) -> String {
69        let marked_actual = mark_unexpected(actual, format);
70        let (not, marked_expected) = if inverted {
71            let marked_expected_start = match self.expected_range.start_bound() {
72                Bound::Included(start) => format!("< {}", mark_missing(start, format)),
73                Bound::Excluded(start) => format!("<= {}", mark_missing(start, format)),
74                Bound::Unbounded => format!("< {}", mark_missing_string("..", format)),
75            };
76            let marked_expected_end = match self.expected_range.end_bound() {
77                Bound::Included(end) => format!("> {}", mark_missing(end, format)),
78                Bound::Excluded(end) => format!(">= {}", mark_missing(end, format)),
79                Bound::Unbounded => format!("> {}", mark_missing_string("..", format)),
80            };
81
82            (
83                "not ",
84                format!("x {marked_expected_start} || x {marked_expected_end}"),
85            )
86        } else {
87            let marked_expected_start = match self.expected_range.start_bound() {
88                Bound::Included(start) => {
89                    if actual < start {
90                        format!("{} <=", mark_missing(start, format))
91                    } else {
92                        format!("{start:?} <=")
93                    }
94                },
95                Bound::Excluded(start) => {
96                    if actual <= start {
97                        format!("{} <", mark_missing(start, format))
98                    } else {
99                        format!("{start:?} <")
100                    }
101                },
102                Bound::Unbounded => format!("{} <", mark_missing_string("..", format)),
103            };
104            let marked_expected_end = match self.expected_range.end_bound() {
105                Bound::Included(end) => {
106                    if actual > end {
107                        format!("<= {}", mark_missing(end, format))
108                    } else {
109                        format!("<= {end:?}")
110                    }
111                },
112                Bound::Excluded(end) => {
113                    if actual >= end {
114                        format!("< {}", mark_missing(end, format))
115                    } else {
116                        format!("< {end:?}")
117                    }
118                },
119                Bound::Unbounded => format!("< {}", mark_missing_string("..", format)),
120            };
121
122            (
123                "",
124                format!("{marked_expected_start} x {marked_expected_end}"),
125            )
126        };
127
128        format!(
129            "expected {expression} to be {not}within range of {:?}\n   but was: {marked_actual}\n  expected: {marked_expected}",
130            self.expected_range,
131        )
132    }
133}
134
135impl<R, E> Invertible for IsInRange<R, E> {}
136
137#[cfg(test)]
138mod tests;