mockers/
cardinality.rs

1use std::cmp::Ordering;
2use std::ops::{Range, RangeFrom, RangeFull, RangeTo};
3use std::ops::{RangeInclusive, RangeToInclusive};
4
5/// Result of checking call count against cardinality constraints
6#[derive(PartialEq, Eq, Debug)]
7pub enum CardinalityCheckResult {
8    /// Call count doesn't satisfy cardinality constraints, but some larger count can.
9    /// For example, call count 1 doesn't satisfy 2..4 cardinality, but subsequent
10    /// call count will satisfy it, so result for 1 is `Possible`.
11    Possible,
12
13    /// Call count satisfies cardinality constraints, but it's possible that larger count won't.
14    /// For example, call count 3 satisifies 2..4 cardinality, but subsequent call count 4 won't,
15    /// so result for 3 is `Satisfied`.
16    Satisfied,
17
18    /// Call count doesn't satsify cardinality constraints and no larger counts will.
19    /// For example, call count 4 doesn't satisfy 2..4 cardinality and all subsequent call counts
20    /// don't, so result for 4 is `Wrong`.
21    Wrong,
22}
23
24pub trait Cardinality {
25    fn check(&self, count: u32) -> CardinalityCheckResult;
26    fn describe(&self) -> String;
27    fn describe_upper_bound(&self) -> String;
28}
29
30impl Cardinality for u32 {
31    fn check(&self, count: u32) -> CardinalityCheckResult {
32        match count.cmp(self) {
33            Ordering::Equal => CardinalityCheckResult::Satisfied,
34            Ordering::Greater => CardinalityCheckResult::Wrong,
35            Ordering::Less => CardinalityCheckResult::Possible,
36        }
37    }
38
39    fn describe(&self) -> String {
40        match *self {
41            0 => "never called".to_string(),
42            1 => "called exactly one time".to_string(),
43            n => format!("called exactly {} times", n),
44        }
45    }
46
47    fn describe_upper_bound(&self) -> String {
48        if *self == 1 {
49            "called exactly one time".to_string()
50        } else {
51            format!("called exactly {} times", self)
52        }
53    }
54}
55
56impl Cardinality for Range<u32> {
57    fn check(&self, count: u32) -> CardinalityCheckResult {
58        if count < self.start {
59            CardinalityCheckResult::Possible
60        } else if count < self.end {
61            CardinalityCheckResult::Satisfied
62        } else {
63            CardinalityCheckResult::Wrong
64        }
65    }
66
67    fn describe(&self) -> String {
68        match (self.start, self.end) {
69            (_, 0) | (_, 1) => "never called".to_string(),
70            (b, e) => format!("called from {} and less than {} times", b, e),
71        }
72    }
73
74    fn describe_upper_bound(&self) -> String {
75        if self.end == 1 {
76            "never called".to_string()
77        } else {
78            format!("called at most {} times", self.end - 1)
79        }
80    }
81}
82
83impl Cardinality for RangeInclusive<u32> {
84    fn check(&self, count: u32) -> CardinalityCheckResult {
85        if count < *self.start() {
86            CardinalityCheckResult::Possible
87        } else if count <= *self.end() {
88            CardinalityCheckResult::Satisfied
89        } else {
90            CardinalityCheckResult::Wrong
91        }
92    }
93
94    fn describe(&self) -> String {
95        match (*self.start(), *self.end()) {
96            (_, 0) => "never called".to_string(),
97            (_, 1) => "called at most one time".to_string(),
98            (b, e) => format!("called from {} to {} times", b, e),
99        }
100    }
101
102    fn describe_upper_bound(&self) -> String {
103        if *self.end() == 0 {
104            "never called".to_string()
105        } else if *self.end() == 1 {
106            "called at most one time".to_string()
107        } else {
108            format!("called at most {} times", self.end())
109        }
110    }
111}
112
113impl Cardinality for RangeFrom<u32> {
114    fn check(&self, count: u32) -> CardinalityCheckResult {
115        match count.cmp(&self.start) {
116            Ordering::Equal | Ordering::Greater => CardinalityCheckResult::Satisfied,
117            Ordering::Less => CardinalityCheckResult::Possible,
118        }
119    }
120
121    fn describe(&self) -> String {
122        match self.start {
123            0 => "called any number of times".to_string(),
124            1 => "called at least once".to_string(),
125            n => format!("called at least {} times", n),
126        }
127    }
128
129    fn describe_upper_bound(&self) -> String {
130        unreachable!()
131    }
132}
133
134impl Cardinality for RangeTo<u32> {
135    fn check(&self, count: u32) -> CardinalityCheckResult {
136        match count.cmp(&self.end) {
137            Ordering::Less => CardinalityCheckResult::Satisfied,
138            Ordering::Equal | Ordering::Greater => CardinalityCheckResult::Wrong,
139        }
140    }
141
142    fn describe(&self) -> String {
143        match self.end {
144            0 | 1 => "never called".to_string(),
145            n => format!("called less than {} times", n),
146        }
147    }
148
149    fn describe_upper_bound(&self) -> String {
150        if self.end <= 1 {
151            "never called".to_string()
152        } else {
153            format!("called less than {} times", self.end)
154        }
155    }
156}
157
158impl Cardinality for RangeToInclusive<u32> {
159    fn check(&self, count: u32) -> CardinalityCheckResult {
160        match count.cmp(&self.end) {
161            Ordering::Less | Ordering::Equal => CardinalityCheckResult::Satisfied,
162            Ordering::Greater => CardinalityCheckResult::Wrong,
163        }
164    }
165
166    fn describe(&self) -> String {
167        match self.end {
168            0 => "never called".to_string(),
169            1 => "at most once".to_string(),
170            n => format!("called no more than {} times", n),
171        }
172    }
173
174    fn describe_upper_bound(&self) -> String {
175        if self.end == 0 {
176            "never called".to_string()
177        } else if self.end == 1 {
178            "called at most one time".to_string()
179        } else {
180            format!("called at most {} times", self.end)
181        }
182    }
183}
184
185impl Cardinality for RangeFull {
186    fn check(&self, _: u32) -> CardinalityCheckResult {
187        CardinalityCheckResult::Satisfied
188    }
189
190    fn describe(&self) -> String {
191        "called any number of times".to_string()
192    }
193
194    fn describe_upper_bound(&self) -> String {
195        unreachable!()
196    }
197}
198
199pub struct Never;
200pub fn never() -> Never {
201    Never
202}
203impl Cardinality for Never {
204    fn check(&self, count: u32) -> CardinalityCheckResult {
205        if count == 0 {
206            CardinalityCheckResult::Satisfied
207        } else {
208            CardinalityCheckResult::Wrong
209        }
210    }
211
212    fn describe(&self) -> String {
213        "never called".to_string()
214    }
215
216    fn describe_upper_bound(&self) -> String {
217        "never called".to_string()
218    }
219}