refinement_types/
int.rs

1//! Predicates for integers.
2
3use 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                /// The value against which the check was performed (the `N`).
88                pub against: $type,
89            }
90
91            impl [< $prefix $name Error >] {
92                /// Constructs [`Self`].
93                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            /// Checks whether the given value is zero.
214            pub type [< $prefix Zero >] = [< $prefix Eq >]<0>;
215
216            /// Checks whether the given value is non-zero.
217            pub type [< $prefix NonZero >] = [< $prefix Ne >]<0>;
218        }
219    };
220}
221
222macro_rules! div_mod {
223    ($type: ty, $prefix: ident) => {
224        paste! {
225            /// Represents errors when the provided value divided by [`divisor`] does not equal [`modulo`].
226            ///
227            /// [`divisor`]: Self::divisor
228            /// [`modulo`]: Self::modulo
229            #[derive(Debug, Error)]
230            #[error("received value % {divisor} != {modulo}")]
231            pub struct [< $prefix Mod Error >] {
232                /// The divisor that the value should be divided by (the `D`).
233                pub divisor: $type,
234                /// The expected modulo of the division (the `M`).
235                pub modulo: $type,
236            }
237
238            impl [< $prefix Mod Error >] {
239                /// Constructs [`Self`].
240                pub const fn new(divisor: $type, modulo: $type) -> Self {
241                    Self { divisor, modulo }
242                }
243            }
244
245            /// Checks whether the given value divided by `D` has modulo `M`.
246            #[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            /// Checks whether the given value is divisible by `D`.
266            pub type [< $prefix Div >]<const D: $type> = [< $prefix Mod >]<D, 0>;
267
268            /// Checks whether the given value is even.
269            pub type [< $prefix Even >] = [< $prefix Div >]<2>;
270
271            /// Checks whether the given value is odd.
272            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            /// Checks whether the given value is positive.
299            pub type [< $prefix Positive >] = [< $prefix Gt >]<0>;
300
301            /// Checks whether the given value is negative.
302            pub type [< $prefix Negative >] = [< $prefix Lt >]<0>;
303
304            /// Checks whether the given value is non-positive (negative or zero).
305            pub type [< $prefix NonPositive >] = [< $prefix Le >]<0>;
306
307            /// Checks whether the given value is non-negative (positive or zero).
308            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);