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}