konst/cmp/
const_cmp.rs

1use crate::cmp::{CmpWrapper, IsNotStdKind, IsStdKind};
2
3use core::marker::PhantomData;
4
5use typewit::TypeEq;
6
7////////////////////////////////////////////////////////////////////////////////
8
9/// Marker trait for types that implement the const comparison methods.
10///
11/// # Implementors
12///
13/// Types that implement this trait are also expected to implement at least one of
14/// these inherent methods:
15///
16/// ```rust
17/// use std::cmp::Ordering;
18///
19/// # struct Foo;
20/// # impl Foo {
21/// const fn const_eq(&self, other: &Self) -> bool
22/// # { true }
23///
24/// const fn const_cmp(&self, other: &Self) -> Ordering
25/// # { Ordering::Equal }
26/// # }
27///
28/// ```
29///
30/// # Coercions
31///
32/// The [`Kind`](#associatedtype.Kind) associated type
33/// is used in the [`IsAConstCmp`] marker type
34/// to automatically wrap types in [`CmpWrapper`] if they're from the standard library,
35/// otherwise leaving them unwrapped.
36///
37///
38/// # Example
39///
40/// ### Manual Implementation
41///
42/// ```
43/// use konst::{
44///     cmp::{ConstCmp, IsNotStdKind, const_cmp, const_eq, try_equal},
45///     assertc_eq, assertc_ne
46/// };
47///
48/// use std::cmp::Ordering;
49///
50///
51/// struct MyType {
52///     x: &'static str,
53///     y: &'static [u16],
54/// }
55///
56/// impl ConstCmp for MyType {
57///     type Kind = IsNotStdKind;
58///     type This = Self;
59/// }
60///
61/// impl MyType {
62///     pub const fn const_eq(&self, other: &Self) -> bool {
63///         const_eq!(self.x, other.x) &&
64///         const_eq!(self.y, other.y)
65///     }
66///
67///     pub const fn const_cmp(&self, other: &Self) -> Ordering {
68///         try_equal!(const_cmp!(self.x, other.x));
69///         try_equal!(const_cmp!(self.y, other.y))
70///     }
71/// }
72///
73/// const _: () = {
74///     let foo = MyType{x: "hello", y: &[3, 5, 8, 13]};
75///     let bar = MyType{x: "world", y: &[3, 5, 8, 13]};
76///
77///     assertc_eq!(const_cmp!(foo, foo), Ordering::Equal);
78///     assertc_eq!(const_cmp!(foo, bar), Ordering::Less);
79///     assertc_eq!(const_cmp!(bar, foo), Ordering::Greater);
80///     assert!(const_eq!(foo, foo));
81///     assert!(!const_eq!(foo, bar));
82/// };
83/// ```
84///
85///
86/// ### `ìmpl_cmp`-based Implementation
87///
88/// You can use [`impl_cmp`] to implement this trait,
89/// as well as define the same methods for
90/// multiple implementations with different type arguments.
91///
92/// ```
93/// use konst::cmp::{const_cmp, const_eq, impl_cmp, try_equal};
94/// use konst::{assertc_eq, assertc_ne};
95///
96/// use std::cmp::Ordering;
97///
98///
99/// struct MyType<'a, T> {
100///     x: &'a str,
101///     y: &'a [T],
102/// }
103///
104/// impl_cmp!{
105///     // The comparison functions are only implemented for these types.
106///     impl['a] MyType<'a, bool>;
107///     impl['a] MyType<'a, u16>;
108///     impl['a] MyType<'a, &'static str>;
109///
110///     pub const fn const_eq(&self, other: &Self) -> bool {
111///         const_eq!(self.x, other.x) &&
112///         const_eq!(self.y, other.y)
113///     }
114///
115///     pub const fn const_cmp(&self, other: &Self) -> Ordering {
116///         try_equal!(const_cmp!(self.x, other.x));
117///         try_equal!(const_cmp!(self.y, other.y))
118///     }
119/// }
120///
121/// const _: () = {
122///     let foo = MyType{x: "hello", y: &[3, 5, 8, 13]};
123///     let bar = MyType{x: "world", y: &[3, 5, 8, 13]};
124///
125///     assertc_eq!(const_cmp!(foo, foo), Ordering::Equal);
126///     assertc_eq!(const_cmp!(foo, bar), Ordering::Less);
127///     assertc_eq!(const_cmp!(bar, foo), Ordering::Greater);
128///     assert!(const_eq!(foo, foo));
129///     assert!(!const_eq!(foo, bar));
130/// };
131///
132/// const _: () = {
133///     let foo = MyType{x: "hello", y: &[false]};
134///     let bar = MyType{x: "hello", y: &[true]};
135///
136///     assertc_eq!(const_cmp!(foo, foo), Ordering::Equal);
137///     assertc_eq!(const_cmp!(foo, bar), Ordering::Less);
138///     assertc_eq!(const_cmp!(bar, foo), Ordering::Greater);
139///     assert!(const_eq!(foo, foo));
140///     assert!(!const_eq!(foo, bar));
141/// };
142///
143/// ```
144///
145/// [`CmpWrapper`]: crate::cmp::CmpWrapper
146/// [`impl_cmp`]: crate::cmp::impl_cmp
147pub trait ConstCmp {
148    /// What kind of type this is, this can be one of:
149    ///
150    /// - [`IsStdKind`]: A standard library type.
151    ///
152    /// - [`IsNotStdKind`]: A type that is not from the standard library.
153    ///
154    type Kind: ConstCmpKind;
155
156    /// The type after dereferencing all references.
157    ///
158    /// User-defined types should generally set this to `Self`.
159    type This: ?Sized + ConstCmp<Kind = Self::Kind, This = Self::This>;
160}
161
162///////////////////////////////////////////////////////////////////////////////
163
164impl<T, const N: usize> ConstCmp for [T; N] {
165    type Kind = IsStdKind;
166    type This = Self;
167}
168
169impl<T> ConstCmp for [T] {
170    type Kind = IsStdKind;
171    type This = Self;
172}
173
174impl ConstCmp for str {
175    type Kind = IsStdKind;
176    type This = Self;
177}
178
179impl<T> ConstCmp for &T
180where
181    T: ?Sized + ConstCmp,
182{
183    type Kind = T::Kind;
184    type This = T::This;
185}
186
187impl<T> ConstCmp for &mut T
188where
189    T: ?Sized + ConstCmp,
190{
191    type Kind = T::Kind;
192    type This = T::This;
193}
194
195///////////////////////////////////////////////////////////////////////////////
196
197/// Hack used to automatically wrap standard library types inside [`CmpWrapper`],
198/// while leaving user defined types unwrapped.
199///
200/// You're not intended to use this directly, the intended way to coerce a type
201/// into a type with a `const_eq` and/or `const_cmp` method is
202/// with the [`coerce_to_cmp`] macro.
203///
204/// # Type parameters
205///
206/// `K` is `<T as ConstCmp>::Kind`
207/// The kind of type that `T` is: either [`IsStdKind`] or
208/// [`IsNotStdKind`](crate::cmp::IsNotStdKind).
209///
210/// `T` is `<R as ConstCmp>::This`,
211/// the `R` type after removing all layers of references.
212///
213/// `R`: Is a type that implements [`ConstCmp`]
214///
215/// [`coerce_to_cmp`]: crate::cmp::coerce_to_cmp
216#[allow(clippy::type_complexity)]
217pub struct IsAConstCmp<K, T: ?Sized, R: ?Sized>(
218    PhantomData<(
219        PhantomData<fn() -> PhantomData<K>>,
220        PhantomData<fn() -> PhantomData<T>>,
221        PhantomData<fn() -> PhantomData<R>>,
222    )>,
223);
224
225impl<K, T: ?Sized, R: ?Sized> Copy for IsAConstCmp<K, T, R> {}
226
227impl<K, T: ?Sized, R: ?Sized> Clone for IsAConstCmp<K, T, R> {
228    fn clone(&self) -> Self {
229        *self
230    }
231}
232
233impl<R> IsAConstCmp<R::Kind, R::This, R>
234where
235    R: ?Sized + ConstCmp,
236{
237    /// Constructs an `IsAConstCmp`
238    pub const NEW: Self = Self(PhantomData);
239}
240
241impl<K, T: ?Sized, R: ?Sized> IsAConstCmp<K, T, R> {
242    /// Infers the type parameters by taking a reference to `R` .
243    ///
244    /// The `K` and `T` type parameters are determined by `R` in
245    /// the [`NEW`] associated constant.
246    ///
247    /// [`NEW`]: #associatedconstant.NEW
248    #[inline(always)]
249    pub const fn infer_type(self, _: &R) -> Self {
250        self
251    }
252
253    /// coerces `&T` into as reference to a type that has a `const_eq` and/or
254    /// `const_cmp` method.
255    ///
256    /// The return type can be either:
257    /// - `&CmpWrapper<T>` if `R::Kind == `[`IsStdKind`].
258    /// - `&T` if `R::Kind == `[`IsNotStdKind`].
259    #[inline]
260    pub const fn coerce<'a>(self, reff: &'a T) -> CoerceTo<'a, T, K>
261    where
262        K: ConstCmpKind,
263    {
264        match const { <K as ConstCmpKind>::__KIND_WITNESS.to_coercion_witness::<'a, T>() } {
265            __CoercionWitness::IsStdKind(te) => te.to_left(CmpWrapper::from_ref(reff)),
266            __CoercionWitness::IsNotStdKind(te) => te.to_left(reff),
267        }
268    }
269
270    /// Removes layers of references by coercing the argument.
271    #[inline(always)]
272    pub const fn unreference(self, r: &T) -> &T {
273        r
274    }
275}
276
277/////////////////////////////////////////////////////////////////////////////
278
279/// Trait for the types that are assignable to [`ConstCmp::Kind`]
280pub trait ConstCmpKind: Sized {
281    /// Maps the pointee of the reference for [`IsAConstCmp::coerce`].
282    /// - `CmpWrapper<T>` if `Self == `[`IsStdKind`].
283    /// - `T` if `Self == `[`IsNotStdKind`].
284    type CoerceTo<T: ?Sized>: ?Sized;
285
286    #[doc(hidden)]
287    const __KIND_WITNESS: __ConstCmpKindWitness<Self>;
288}
289
290impl ConstCmpKind for IsStdKind {
291    type CoerceTo<T: ?Sized> = CmpWrapper<T>;
292
293    #[doc(hidden)]
294    const __KIND_WITNESS: __ConstCmpKindWitness<Self> =
295        __ConstCmpKindWitness::IsStdKind(TypeEq::NEW);
296}
297
298impl ConstCmpKind for IsNotStdKind {
299    type CoerceTo<T: ?Sized> = T;
300
301    #[doc(hidden)]
302    const __KIND_WITNESS: __ConstCmpKindWitness<Self> =
303        __ConstCmpKindWitness::IsNotStdKind(TypeEq::NEW);
304}
305
306/// What `IsAConstCmp::coerce` coerces `&'a T` into, it can be either:
307/// - `&'a CmpWrapper<T>` if `<T as ConstCmp>::Kind` == [`IsStdKind`].
308/// - `&'a T` if `<T as ConstCmp>::Kind` == [`IsNotStdKind`].
309pub type CoerceTo<'a, T, K = <T as ConstCmp>::Kind> = &'a <K as ConstCmpKind>::CoerceTo<T>;
310
311#[doc(hidden)]
312pub enum __ConstCmpKindWitness<Kind> {
313    IsStdKind(TypeEq<Kind, IsStdKind>),
314    IsNotStdKind(TypeEq<Kind, IsNotStdKind>),
315}
316
317impl<Kind> Copy for __ConstCmpKindWitness<Kind> {}
318
319impl<Kind> Clone for __ConstCmpKindWitness<Kind> {
320    fn clone(&self) -> Self {
321        *self
322    }
323}
324
325impl<Kind> __ConstCmpKindWitness<Kind> {
326    const fn to_coercion_witness<'a, T>(self) -> __CoercionWitness<'a, T, Kind>
327    where
328        T: ?Sized,
329        Kind: ConstCmpKind,
330    {
331        typewit::type_fn! {
332            struct CoerceToFn<T: ?Sized>;
333            impl<K: ConstCmpKind> K => <K as ConstCmpKind>::CoerceTo<T>
334        }
335
336        match self {
337            Self::IsStdKind(te) => {
338                __CoercionWitness::IsStdKind(te.project::<CoerceToFn<T>>().in_ref())
339            }
340            Self::IsNotStdKind(te) => {
341                __CoercionWitness::IsNotStdKind(te.project::<CoerceToFn<T>>().in_ref())
342            }
343        }
344    }
345}
346
347#[doc(hidden)]
348enum __CoercionWitness<'a, T: ?Sized, K: ConstCmpKind> {
349    IsStdKind(TypeEq<CoerceTo<'a, T, K>, &'a CmpWrapper<T>>),
350    IsNotStdKind(TypeEq<CoerceTo<'a, T, K>, &'a T>),
351}
352
353/////////////////////////////////////////////////////////////////////////////
354
355#[doc(hidden)]
356pub struct __AssertConstCmp<'a, T: ConstCmp> {
357    pub reff: &'a T,
358}
359
360#[doc(hidden)]
361#[macro_export]
362macro_rules! __assert_const_cmp {
363    ($reff:expr) => {
364        $crate::cmp::__AssertConstCmp { reff: $reff }
365    };
366}