konst/macros/
const_ord_macros.rs

1/// Compares two values for ordering.
2///
3/// The arguments must implement the [`ConstCmpMarker`] trait.
4/// Non-standard library types must define a `const_cmp` method taking a reference.
5///
6/// # Limitations
7///
8/// The arguments must be concrete types, and have a fully inferred type.
9/// eg: if you pass an integer literal it must have a suffix to indicate its type.
10///
11/// # Example
12///
13/// ```rust
14/// use konst::{const_cmp, impl_cmp, try_equal};
15///
16/// use std::cmp::Ordering;
17///
18/// struct Fields<'a> {
19///     foo: u32,
20///     bar: Option<bool>,
21///     baz: Ordering,
22///     qux: &'a str,
23/// }
24///
25/// impl_cmp!{
26///     impl['a] Fields<'a>;
27///     pub const fn const_cmp(&self, other: &Self) -> Ordering {
28///         try_equal!(const_cmp!(self.foo, other.foo));
29///         try_equal!(const_cmp!(self.bar, other.bar));
30///         try_equal!(const_cmp!(self.baz, other.baz));
31///         try_equal!(const_cmp!(self.qux, other.qux))
32///     }
33/// }
34///
35/// const CMPS: [Ordering; 4] = {
36///     let foo = Fields {
37///         foo: 10,
38///         bar: None,
39///         baz: Ordering::Less,
40///         qux: "hello",
41///     };
42///     
43///     let bar = Fields {
44///         foo: 99,
45///         bar: Some(true),
46///         baz: Ordering::Greater,
47///         qux: "world",
48///     };
49///     
50///     [const_cmp!(foo, foo), const_cmp!(foo, bar), const_cmp!(bar, foo), const_cmp!(bar, bar)]
51/// };
52///
53/// assert_eq!(CMPS, [Ordering::Equal, Ordering::Less, Ordering::Greater, Ordering::Equal]);
54///
55///
56///
57/// ```
58///
59/// [`ConstCmpMarker`]: ./polymorphism/trait.ConstCmpMarker.html
60#[cfg(feature = "cmp")]
61#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
62#[macro_export]
63macro_rules! const_cmp {
64    ($left:expr, $right:expr $(,)*) => {
65        match $crate::coerce_to_cmp!($left, $right) {
66            (left, right) => left.const_cmp(right),
67        }
68    };
69}
70
71/// Compares two standard library types for ordering,
72/// that can't be compared with [`const_cmp`].
73///
74/// This macro takes the same
75/// [types](macro.const_eq_for.html#types-section) (except for range types),
76/// has the same  [limitations](macro.const_eq_for.html#limitations-section),
77/// and takes [arguments of the same form](macro.const_eq_for.html#arguments-section)
78/// as the [`const_eq_for`] macro
79///
80/// # Examples
81///
82/// ### Slices
83///
84/// ```rust
85/// use konst::{const_cmp, const_cmp_for, try_equal};
86///
87/// use std::cmp::Ordering;
88///
89/// const fn cmp_slice_pair(left: &[(u32, u32)], right: &[(u32, u32)]) -> Ordering {
90///     const_cmp_for!(slice; left, right, |l, r|{
91///         try_equal!(const_cmp!(l.0, r.0));
92///         try_equal!(const_cmp!(l.1, r.1))
93///     })
94/// }
95///
96/// const CMPS: [Ordering; 4] = {
97///     let foo = &[(0, 1), (1, 2), (3, 4), (5, 6)];
98///     let bar = &[(0, 1), (3, 4), (5, 6), (7, 8)];
99///
100///     [
101///         cmp_slice_pair(foo, foo),
102///         cmp_slice_pair(foo, bar),
103///         cmp_slice_pair(bar, foo),
104///         cmp_slice_pair(bar, bar),
105///     ]
106/// };
107///
108/// assert_eq!(CMPS, [Ordering::Equal, Ordering::Less, Ordering::Greater, Ordering::Equal])
109///
110/// ```
111///
112///
113/// ### Options
114///
115/// ```rust
116/// use konst::{const_cmp, const_cmp_for, try_equal};
117///
118/// use std::cmp::Ordering;
119///
120/// #[derive(Copy, Clone)]
121/// enum Shape {
122///     Square,
123///     Circle,
124///     Line,
125/// }
126///
127/// const fn cmp_opt_pair(left: Option<Shape>, right: Option<Shape>) -> Ordering {
128///     const_cmp_for!(option; left, right, |x| *x as u8 )
129/// }
130///
131/// const CMPS: [Ordering; 9] = {
132///     let foo = Some(Shape::Square);
133///     let bar = Some(Shape::Circle);
134///     let baz = Some(Shape::Line);
135///
136///     [
137///         cmp_opt_pair(foo, foo),
138///         cmp_opt_pair(foo, bar),
139///         cmp_opt_pair(foo, baz),
140///
141///         cmp_opt_pair(bar, foo),
142///         cmp_opt_pair(bar, bar),
143///         cmp_opt_pair(bar, baz),
144///
145///         cmp_opt_pair(baz, foo),
146///         cmp_opt_pair(baz, bar),
147///         cmp_opt_pair(baz, baz),
148///     ]
149/// };
150///
151/// assert_eq!(
152///     CMPS,
153///     [
154///         Ordering::Equal, Ordering::Less, Ordering::Less,
155///         Ordering::Greater, Ordering::Equal, Ordering::Less,
156///         Ordering::Greater, Ordering::Greater, Ordering::Equal,
157///     ]
158/// );
159///
160/// ```
161///
162/// [`ConstCmpMarker`]: ./polymorphism/trait.ConstCmpMarker.html
163/// [`const_cmp`]: macro.const_cmp.html
164/// [`cmp::Ordering`]: https://doc.rust-lang.org/core/cmp/enum.Ordering.html
165///
166#[cfg(feature = "cmp")]
167#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
168#[macro_export]
169macro_rules! const_cmp_for {
170    (
171        slice;
172        $left_slice:expr,
173        $right_slice:expr
174        $(, $($comparison:tt)* )?
175    ) => {
176        match ($left_slice, $right_slice) {(mut left_slice, mut right_slice) => {
177            use $crate::__::Ordering as CmpOrdering;
178            if left_slice.len() == right_slice.len() {
179                loop{
180                    if let ([l, l_rem@..], [r, r_rem@..]) = (left_slice, right_slice) {
181                        left_slice = l_rem;
182                        right_slice = r_rem;
183
184                        let ord = $crate::__priv_const_cmp_for!{
185                            *l,
186                            *r,
187                            $($($comparison)*)?
188                        };
189                        if !$crate::__::matches!(ord, $crate::__::Ordering::Equal) {
190                            break ord;
191                        }
192                    } else {
193                        break $crate::__::Ordering::Equal
194                    }
195                }
196            } else if left_slice.len() < right_slice.len() {
197                CmpOrdering::Less
198            } else {
199                CmpOrdering::Greater
200            }
201        }}
202    };
203    (
204        option;
205        $left_opt:expr,
206        $right_opt:expr
207        $(, $($comparison:tt)* )?
208    ) => {
209        match (&$left_opt, &$right_opt) {
210            (Some(l), Some(r)) =>
211                $crate::__priv_const_cmp_for!(*l, *r, $( $($comparison)* )?),
212            (Some(_), None) => $crate::__::Greater,
213            (None, Some(_)) => $crate::__::Less,
214            (None, None) => $crate::__::Equal,
215        }
216    };
217}
218
219#[doc(hidden)]
220#[macro_export]
221macro_rules! __priv_const_cmp_for {
222    ($left:expr, $right:expr, ) => {
223        $crate::coerce_to_cmp!(&$left).const_cmp(&$right)
224    };
225    ($left:expr, $right:expr, |$l: pat| $key_expr:expr $(,)*) => {
226        $crate::coerce_to_cmp!({
227            let $l = &$left;
228            $key_expr
229        })
230        .const_cmp(&{
231            let $l = &$right;
232            $key_expr
233        })
234    };
235    ($left:expr, $right:expr, |$l: pat, $r: pat| $eq_expr:expr $(,)*) => {{
236        let $l = &$left;
237        let $r = &$right;
238        $eq_expr
239    }};
240    ($left:expr, $right:expr, $func:path $(,)*) => {{
241        $func(&$left, &$right)
242    }};
243}
244
245/// Evaluates to `$ord` if it is `Ordering::Equal`,
246/// otherwise returns it from the enclosing function.
247///
248/// # Example
249///
250/// ```rust
251/// use konst::{const_cmp, impl_cmp, try_equal};
252///
253/// use std::cmp::Ordering;
254///
255/// struct Fields<'a> {
256///     first: &'a [u8; 4],
257///     second: bool,
258///     third: Option<&'static str>,
259/// }
260///
261/// impl_cmp!{
262///     impl['a] Fields<'a>;
263///     pub const fn const_cmp(&self, other: &Self) -> Ordering {
264///         try_equal!(const_cmp!(self.first, other.first));
265///         try_equal!(const_cmp!(self.second, other.second));
266///         try_equal!(const_cmp!(self.third, other.third))
267///     }
268/// }
269///
270/// const CMPS: [Ordering; 4] = {
271///     let foo = Fields {
272///         first: &[3, 5, 8, 13],
273///         second: false,
274///         third: None,
275///     };
276///     
277///     let bar = Fields {
278///         first: &[5, 8, 13, 14],
279///         second: true,
280///         third: Some("what!?"),
281///     };
282///     
283///     [const_cmp!(foo, foo), const_cmp!(foo, bar), const_cmp!(bar, foo), const_cmp!(bar, bar)]
284/// };
285///
286/// assert_eq!(CMPS, [Ordering::Equal, Ordering::Less, Ordering::Greater, Ordering::Equal]);
287///
288/// ```
289///
290#[cfg(feature = "cmp")]
291#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
292#[macro_export]
293macro_rules! try_equal {
294    (break $ord:expr $(,)*) => {
295        match $ord {
296            $crate::__::Ordering::Equal => $crate::__::Ordering::Equal,
297            ord => return ord,
298        }
299    };
300    ($ord:expr $(,)*) => {
301        match $ord {
302            $crate::__::Ordering::Equal => $crate::__::Ordering::Equal,
303            ord => return ord,
304        }
305    };
306    (break; $ord:expr $(,)*) => {
307        match $ord {
308            $crate::__::Ordering::Equal => $crate::__::Ordering::Equal,
309            ord => return ord,
310        }
311    };
312}
313
314#[cfg(feature = "cmp")]
315macro_rules! cmp_int {
316    ($l:expr, $r:expr $(,)*) => {{
317        if $l == $r {
318            Ordering::Equal
319        } else if $l < $r {
320            Ordering::Less
321        } else {
322            Ordering::Greater
323        }
324    }};
325}