konst/macros/
const_eq_macros.rs

1/// Compares two values for equality.
2///
3/// The arguments must implement the [`ConstCmpMarker`] trait.
4/// Non-standard library types must define a `const_eq` 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_eq, impl_cmp};
15///
16/// use std::ops::Range;
17///
18/// struct Fields<'a> {
19///     foo: u32,
20///     bar: Option<bool>,
21///     baz: Range<usize>,
22///     qux: &'a str,
23/// }
24///
25/// impl_cmp!{
26///     impl['a] Fields<'a>;
27///     pub const fn const_eq(&self, other: &Self) -> bool {
28///         self.foo == other.foo &&
29///         const_eq!(self.bar, other.bar) &&
30///         const_eq!(self.baz, other.baz) &&
31///         const_eq!(self.qux, other.qux)
32///     }
33/// }
34///
35/// const CMPS: [bool; 4] = {
36///     let foo = Fields {
37///         foo: 10,
38///         bar: None,
39///         baz: 10..20,
40///         qux: "hello",
41///     };
42///     
43///     let bar = Fields {
44///         foo: 99,
45///         bar: Some(true),
46///         baz: 0..5,
47///         qux: "world",
48///     };
49///     
50///     [const_eq!(foo, foo), const_eq!(foo, bar), const_eq!(bar, foo), const_eq!(bar, bar)]
51/// };
52///
53/// assert_eq!(CMPS, [true, false, false, true]);
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_eq {
64    ($left:expr, $right:expr $(,)*) => {
65        match $crate::coerce_to_cmp!($left, $right) {
66            (left, right) => left.const_eq(right),
67        }
68    };
69}
70
71/// Compares two standard library types for equality,
72/// that can't be compared with [`const_eq`].
73///
74/// <span id = "types-section"></span>
75/// # Types
76///
77/// This macro supports multiple types with different prefixes:
78///
79/// - `slice`: for comparing `&[T]`. [example](#compare_slices_structs)
80///
81/// - `option`: for comparing `Option<T>`. [example](#compare_options)
82///
83/// - `range`: for comparing `Range<T>`. [example](#compare_ranges)
84///
85/// - `range_inclusive`: for comparing `RangeInclusive<T>`.
86/// [example](#compare_ranges_incluside)
87///
88/// <span id = "limitations-section"></span>
89/// # Limitations
90///
91/// The arguments must be concrete types, and have a fully inferred type.
92/// eg: if you pass an integer literal it must have a suffix to indicate its type.
93///
94/// <span id = "arguments-section"></span>
95/// # Arguments
96///
97/// The arguments take this form
98///
99/// ```text
100/// const_eq_for!(type; left_value, right_value <comparator> )
101/// ```
102///
103/// ### Comparator argument
104///
105/// The `<comparator>` argument can be any of:
106///
107/// - ` `(passing nothing): Compares the item using the [`const_eq`] macro.
108/// [example](#compare_slices_structs)
109///
110/// - `, |item| <expression>`:
111/// Converts the item with `<expression>` to a type that can be compared using the
112/// [`const_eq`] macro.
113/// [example](#compare_slices_fieldless_enums)
114///
115/// - `, |left_item, right_item| <expression>`:
116/// Compares the items  with `<expression>`, which must evaluate to a `bool`.
117/// [example](#compare_options)
118///
119/// - `, path::to::function`:
120/// Compares the items using the passed function, which must evaluate to a `bool`.
121/// [example](#compare_ranges_incluside)
122///
123///
124///
125/// # Examples
126///
127/// <span id = "compare_slices_structs"></span>
128/// ### Comparing slices of structs
129///
130/// ```
131/// use konst::{const_eq_for, eq_str};
132///
133/// #[derive(Debug, Copy, Clone, PartialEq, Eq)]
134/// pub struct Location {
135///     pub file: &'static str,
136///     pub column: u32,
137///     pub line: u32,
138/// }
139///
140/// konst::impl_cmp! {
141///     impl Location;
142///     
143///     pub const fn const_eq(&self, other: &Self) -> bool {
144///         eq_str(self.file, other.file) &&
145///         self.column == other.column &&
146///         self.line == other.line
147///     }
148/// }
149/// #
150/// # macro_rules! here {
151/// #   () => {
152/// #       $crate::Location{file: file!(), column: column!(), line: line!()}
153/// #   }
154/// # }
155/// #
156///
157/// # fn main () {
158/// const HERE: &[Location] = &[here!(), here!(), here!(), here!()];
159///
160/// const THERE: &[Location] = &[here!(), here!(), here!(), here!()];
161///
162/// const CMP_HERE: bool = const_eq_for!(slice; HERE, HERE);
163/// assert!( CMP_HERE );
164///
165/// const CMP_HERE_THERE: bool = const_eq_for!(slice; HERE, THERE);
166/// assert!( !CMP_HERE_THERE );
167///
168/// const CMP_THERE_THERE: bool = const_eq_for!(slice; THERE, THERE);
169/// assert!( CMP_THERE_THERE );
170///
171///
172/// # }
173///
174/// ```
175///
176/// <span id = "compare_slices_fieldless_enums"></span>
177/// ### Comparing slices of field-less enums
178///
179/// ```rust
180/// #[derive(Copy, Clone)]
181/// enum Direction {
182///     Left,
183///     Right,
184///     Up,
185///     Down,
186/// }
187///
188/// use Direction::*;
189///
190/// const fn eq_slice_direction(left: &[Direction], right: &[Direction]) -> bool {
191///     konst::const_eq_for!(slice; left, right, |&x| x as u8)
192/// }
193///
194/// const CHEAT_CODE: &[Direction] = &[Up, Up, Down, Down, Left, Right, Left, Right];
195///
196/// const CLOCKWISE: &[Direction] = &[Up, Right, Down, Left];
197///
198///
199/// const CMP_CHEAT: bool = eq_slice_direction(CHEAT_CODE, CHEAT_CODE);
200/// assert!( CMP_CHEAT );
201///
202/// const CMP_CHEAT_CLOCK: bool = eq_slice_direction(CHEAT_CODE, CLOCKWISE);
203/// assert!( !CMP_CHEAT_CLOCK );
204///
205/// const CMP_CLOCK_CLOCK: bool = eq_slice_direction(CLOCKWISE, CLOCKWISE);
206/// assert!( CMP_CLOCK_CLOCK );
207///
208/// ```
209///
210/// <span id = "compare_options"></span>
211/// ### Comparing `Option`s
212///
213/// ```rust
214/// use konst::const_eq_for;
215///
216/// const SOME: Option<(u32, u32)> = Some((3, 5));
217/// const NONE: Option<(u32, u32)> = None;
218///
219/// const fn eq_opt_tuple(left: &Option<(u32, u32)>, right: &Option<(u32, u32)>) -> bool {
220///     const_eq_for!(option; left, right, |l, r| l.0 == r.0 && l.1 == r.1 )
221/// }
222///
223/// const SOME_SOME: bool = eq_opt_tuple(&SOME, &SOME);
224/// assert!( SOME_SOME );
225///
226/// const SOME_NONE: bool = eq_opt_tuple(&SOME, &NONE);
227/// assert!( !SOME_NONE );
228///
229/// const NONE_NONE: bool = eq_opt_tuple(&NONE, &NONE);
230/// assert!( NONE_NONE );
231///
232/// ```
233///
234///
235/// <span id = "compare_ranges"></span>
236/// ### Comparing `Range`s
237///
238/// ```rust
239/// use konst::{const_eq_for, impl_cmp};
240///
241/// use std::ops::Range;
242///
243/// #[derive(Copy, Clone)]
244/// pub enum Month {
245///     January,
246///     February,
247///     March,
248///     April,
249///     May,
250///     June,
251///     July,
252///     August,
253///     September,
254///     October,
255///     November,
256///     December,
257/// }
258///
259/// use Month::*;
260///
261/// konst::impl_cmp! {
262///     impl Month;
263///     
264///     pub const fn const_eq(&self, other: &Self) -> bool {
265///         *self as u8 == *other as u8
266///     }
267/// }
268///
269/// const FOO: Range<Month> = January..April;
270/// const BAR: Range<Month> = October..December;
271///
272/// const FOO_FOO: bool = const_eq_for!(range; FOO, FOO);
273/// assert!( FOO_FOO );
274///
275/// const FOO_BAR: bool = const_eq_for!(range; FOO, BAR);
276/// assert!( !FOO_BAR );
277///
278/// const BAR_BAR: bool = const_eq_for!(range; BAR, BAR);
279/// assert!( BAR_BAR );
280///
281/// ```
282///
283/// <span id = "compare_ranges_incluside"></span>
284/// ### Comparing `RangeInclusive`s
285///
286/// ```rust
287/// use konst::{const_eq_for, impl_cmp};
288///
289/// use std::ops::RangeInclusive;
290///
291/// #[derive(Copy, Clone)]
292/// pub enum WeekDay {
293///     Monday,
294///     Tuesday,
295///     Wednesday,
296///     Thursday,
297///     Friday,
298///     Saturday,
299///     Sunday,
300/// }
301///
302/// use WeekDay::*;
303///
304/// konst::impl_cmp! {
305///     impl WeekDay;
306///     
307///     pub const fn const_eq(&self, other: &Self) -> bool {
308///         *self as u8 == *other as u8
309///     }
310/// }
311///
312/// const FOO: RangeInclusive<WeekDay> = Monday..=Thursday;
313/// const BAR: RangeInclusive<WeekDay> = Friday..=Sunday;
314///
315/// const FOO_FOO: bool = const_eq_for!(range_inclusive; FOO, FOO);
316/// assert!( FOO_FOO );
317///
318/// const FOO_BAR: bool = const_eq_for!(range_inclusive; FOO, BAR, WeekDay::const_eq);
319/// assert!( !FOO_BAR );
320///
321/// const BAR_BAR: bool = const_eq_for!(range_inclusive; BAR, BAR, WeekDay::const_eq);
322/// assert!( BAR_BAR );
323///
324/// ```
325///
326/// [`ConstCmpMarker`]: ./polymorphism/trait.ConstCmpMarker.html
327/// [`const_eq`]: macro.const_eq.html
328#[cfg(feature = "cmp")]
329#[cfg_attr(feature = "docsrs", doc(cfg(feature = "cmp")))]
330#[macro_export]
331macro_rules! const_eq_for {
332    (
333        slice;
334        $left_slice:expr,
335        $right_slice:expr
336        $(, $($comparison:tt)* )?
337    ) => {
338        match ($left_slice, $right_slice) {
339            (left_slice, right_slice) => {
340                let mut returned = left_slice.len() == right_slice.len();
341                if returned {
342                    let mut i = 0;
343                    while i != left_slice.len() {
344                        let are_eq = $crate::__priv_const_eq_for!(
345                            left_slice[i],
346                            right_slice[i],
347                            $( $($comparison)* )?
348                        );
349                        if !are_eq {
350                            returned = false;
351                            break;
352                        }
353                        i += 1;
354                    }
355                }
356                returned
357            }
358        }
359    };
360    (
361        option;
362        $left_opt:expr,
363        $right_opt:expr
364        $(, $($comparison:tt)* )?
365    ) => {
366        match (&$left_opt, &$right_opt) {
367            (Some(l), Some(r)) =>
368                $crate::__priv_const_eq_for!(*l, *r, $( $($comparison)* )?),
369            (None, None) => true,
370            _ => false,
371        }
372    };
373    (
374        range;
375        $left_range:expr,
376        $right_range:expr
377        $(, $($comparison:tt)* )?
378    ) => {
379        match (&$left_range, &$right_range) {
380            (left_range, right_range) => {
381                $crate::__priv_const_eq_for!(
382                    left_range.start,
383                    right_range.start,
384                    $( $($comparison)* )?
385                ) &&
386                $crate::__priv_const_eq_for!(
387                    left_range.end,
388                    right_range.end,
389                    $( $($comparison)* )?
390                )
391            }
392        }
393    };
394    (
395        range_inclusive;
396        $left_range:expr,
397        $right_range:expr
398        $(, $($comparison:tt)* )?
399    ) => {
400        match (&$left_range, &$right_range) {
401            (left_range, right_range) => {
402                $crate::__priv_const_eq_for!(
403                    left_range.start(),
404                    right_range.start(),
405                    $( $($comparison)* )?
406                ) &&
407                $crate::__priv_const_eq_for!(
408                    left_range.end(),
409                    right_range.end(),
410                    $( $($comparison)* )?
411                )
412            }
413        }
414    };
415}
416
417#[doc(hidden)]
418#[macro_export]
419macro_rules! __priv_const_eq_for {
420    ($left:expr, $right:expr, ) => {
421        $crate::coerce_to_cmp!($left).const_eq(&$right)
422    };
423    ($left:expr, $right:expr, |$l: pat| $key_expr:expr $(,)*) => {
424        $crate::coerce_to_cmp!({
425            let $l = &$left;
426            $key_expr
427        })
428        .const_eq(&{
429            let $l = &$right;
430            $key_expr
431        })
432    };
433    ($left:expr, $right:expr, |$l: pat, $r: pat| $eq_expr:expr $(,)*) => {{
434        let $l = &$left;
435        let $r = &$right;
436        $eq_expr
437    }};
438    ($left:expr, $right:expr, $func:path $(,)*) => {
439        $func(&$left, &$right)
440    };
441}