1use core::fmt;
4
5use paste::paste;
6use thiserror::Error;
7
8use crate::{
9 core::Predicate,
10 logic::{And, Not},
11};
12
13macro_rules! inverse {
14 (<) => {
15 ">="
16 };
17 (<=) => {
18 ">"
19 };
20 (>) => {
21 "<="
22 };
23 (>=) => {
24 "<"
25 };
26 (==) => {
27 "!="
28 };
29 (!=) => {
30 "=="
31 };
32}
33
34macro_rules! human {
35 (<) => {
36 "less than"
37 };
38 (<=) => {
39 "less than or equal to"
40 };
41 (>) => {
42 "greater than"
43 };
44 (>=) => {
45 "greater than or equal to"
46 };
47 (==) => {
48 "equal to"
49 };
50 (!=) => {
51 "not equal to"
52 };
53}
54
55macro_rules! human_inverse {
56 (<) => {
57 "greater than or equal to"
58 };
59 (<=) => {
60 "greater than"
61 };
62 (>) => {
63 "less than or equal to"
64 };
65 (>=) => {
66 "less than"
67 };
68 (==) => {
69 "not equal to"
70 };
71 (!=) => {
72 "equal to"
73 };
74}
75
76macro_rules! cmp {
77 ($type: ty, $prefix: ident, $name: ident, $operation: tt) => {
78 paste! {
79 #[doc = concat!(
80 "Represents errors that occur when provided value is ",
81 human_inverse!($operation),
82 " some bound.",
83 )]
84 #[derive(Debug, Error)]
85 #[error("received value {operation} {against}", operation = inverse!($operation))]
86 pub struct [< $prefix $name Error >] {
87 pub against: $type,
89 }
90
91 impl [< $prefix $name Error >] {
92 pub const fn new(against: $type) -> Self {
94 Self { against }
95 }
96 }
97
98 #[doc = concat!("Checks whether the given value is ", human!($operation), " `N`.")]
99 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
100 pub struct [< $prefix $name >]<const N: $type>;
101
102 impl<const N: $type> Predicate<$type> for [< $prefix $name >]<N> {
103 type Error = [< $prefix $name Error >];
104
105 fn check(value: &$type) -> Result<(), Self::Error> {
106 if *value $operation N {
107 Ok(())
108 } else {
109 Err(Self::Error::new(N))
110 }
111 }
112
113 fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
114 write!(formatter, "int {operation} {N}", operation = stringify!($operation))
115 }
116 }
117 }
118 };
119}
120
121macro_rules! lt {
122 ($type: ty, $prefix: ident) => {
123 cmp!($type, $prefix, Lt, <);
124 };
125}
126
127macro_rules! le {
128 ($type: ty, $prefix: ident) => {
129 cmp!($type, $prefix, Le, <=);
130 };
131}
132
133macro_rules! gt {
134 ($type: ty, $prefix: ident) => {
135 cmp!($type, $prefix, Gt, >);
136 };
137}
138
139macro_rules! ge {
140 ($type: ty, $prefix: ident) => {
141 cmp!($type, $prefix, Ge, >=);
142 };
143}
144
145macro_rules! eq {
146 ($type: ty, $prefix: ident) => {
147 cmp!($type, $prefix, Eq, ==);
148 };
149}
150
151macro_rules! ne {
152 ($type: ty, $prefix: ident) => {
153 cmp!($type, $prefix, Ne, !=);
154 };
155}
156
157macro_rules! comparisons {
158 ($type: ty, $prefix: ident) => {
159 lt!($type, $prefix);
160 le!($type, $prefix);
161 gt!($type, $prefix);
162 ge!($type, $prefix);
163 eq!($type, $prefix);
164 ne!($type, $prefix);
165 };
166}
167
168macro_rules! interval {
169 ($type: ty, $prefix: ident, $name: ident, $start: ident, $end: ident, $interval: literal) => {
170 paste! {
171 #[doc = concat!("Represents ", $interval, " intervals.")]
172 pub type [< $prefix $name >]<const M: $type, const N: $type> = And<[< $prefix $start >]<M>, [< $prefix $end >]<N>>;
173 }
174 };
175}
176
177macro_rules! open {
178 ($type: ty, $prefix: ident) => {
179 interval!($type, $prefix, Open, Gt, Lt, "`(M, N)`");
180 };
181}
182
183macro_rules! closed_open {
184 ($type: ty, $prefix: ident) => {
185 interval!($type, $prefix, ClosedOpen, Ge, Lt, "`[M, N)`");
186 };
187}
188
189macro_rules! open_closed {
190 ($type: ty, $prefix: ident) => {
191 interval!($type, $prefix, OpenClosed, Gt, Le, "`(M, N]`");
192 };
193}
194
195macro_rules! closed {
196 ($type: ty, $prefix: ident) => {
197 interval!($type, $prefix, Closed, Ge, Le, "`[M, N]`");
198 };
199}
200
201macro_rules! intervals {
202 ($type: ty, $prefix: ident) => {
203 open!($type, $prefix);
204 closed_open!($type, $prefix);
205 open_closed!($type, $prefix);
206 closed!($type, $prefix);
207 };
208}
209
210macro_rules! zeros {
211 ($type: ty, $prefix: ident) => {
212 paste! {
213 pub type [< $prefix Zero >] = [< $prefix Eq >]<0>;
215
216 pub type [< $prefix NonZero >] = [< $prefix Ne >]<0>;
218 }
219 };
220}
221
222macro_rules! div_mod {
223 ($type: ty, $prefix: ident) => {
224 paste! {
225 #[derive(Debug, Error)]
230 #[error("received value % {divisor} != {modulo}")]
231 pub struct [< $prefix Mod Error >] {
232 pub divisor: $type,
234 pub modulo: $type,
236 }
237
238 impl [< $prefix Mod Error >] {
239 pub const fn new(divisor: $type, modulo: $type) -> Self {
241 Self { divisor, modulo }
242 }
243 }
244
245 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
247 pub struct [< $prefix Mod >]<const D: $type, const M: $type>;
248
249 impl<const D: $type, const M: $type> Predicate<$type> for [< $prefix Mod >]<D, M> {
250 type Error = [< $prefix Mod Error >];
251
252 fn check(value: &$type) -> Result<(), Self::Error> {
253 if *value % D == M {
254 Ok(())
255 } else {
256 Err(Self::Error::new(D, M))
257 }
258 }
259
260 fn expect(formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
261 write!(formatter, "int % {D} == {M}")
262 }
263 }
264
265 pub type [< $prefix Div >]<const D: $type> = [< $prefix Mod >]<D, 0>;
267
268 pub type [< $prefix Even >] = [< $prefix Div >]<2>;
270
271 pub type [< $prefix Odd >] = Not<[< $prefix Even >]>;
273 }
274 };
275}
276
277macro_rules! common_predicates {
278 ($type: ty, $prefix: ident) => {
279 comparisons!($type, $prefix);
280
281 intervals!($type, $prefix);
282
283 zeros!($type, $prefix);
284
285 div_mod!($type, $prefix);
286 };
287}
288
289macro_rules! unsigned_predicates {
290 ($type: ty, $prefix: ident) => {
291 common_predicates!($type, $prefix);
292 };
293}
294
295macro_rules! signed {
296 ($type: ty, $prefix: ident) => {
297 paste! {
298 pub type [< $prefix Positive >] = [< $prefix Gt >]<0>;
300
301 pub type [< $prefix Negative >] = [< $prefix Lt >]<0>;
303
304 pub type [< $prefix NonPositive >] = [< $prefix Le >]<0>;
306
307 pub type [< $prefix NonNegative >] = [< $prefix Ge >]<0>;
309 }
310 };
311}
312
313macro_rules! signed_predicates {
314 ($type: ty, $prefix: ident) => {
315 common_predicates!($type, $prefix);
316
317 signed!($type, $prefix);
318 };
319}
320
321unsigned_predicates!(u8, U8);
322unsigned_predicates!(u16, U16);
323unsigned_predicates!(u32, U32);
324unsigned_predicates!(u64, U64);
325unsigned_predicates!(u128, U128);
326unsigned_predicates!(usize, Usize);
327
328signed_predicates!(i8, I8);
329signed_predicates!(i16, I16);
330signed_predicates!(i32, I32);
331signed_predicates!(i64, I64);
332signed_predicates!(i128, I128);
333signed_predicates!(isize, Isize);