Skip to main content

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