1use std::cmp::Ordering;
2use std::ops::{Range, RangeFrom, RangeFull, RangeTo};
3use std::ops::{RangeInclusive, RangeToInclusive};
4
5#[derive(PartialEq, Eq, Debug)]
7pub enum CardinalityCheckResult {
8 Possible,
12
13 Satisfied,
17
18 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}