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}