#![cfg_attr(
any(docsrs, docsrs_dep),
feature(rustdoc_internals),
expect(internal_features, reason = "for `#[doc(fake_variadic)]`")
)]
#![doc = include_str!("../README.md")]
#![no_std]
mod array;
use {
array::{concat, from_fn, map},
const_default::ConstDefault,
core::{
convert::Infallible,
marker::{PhantomData, PhantomPinned},
mem::MaybeUninit,
ops::{Add, Mul},
},
generic_array::{ArrayLength, GenericArray},
typenum::{Const, Pow, Sum, ToUInt, U, U0, U1, U2, Unsigned},
variadics_please::all_tuples,
};
pub use {
const_exhaustive_derive::Exhaustive,
generic_array::{self, const_transmute},
typenum,
};
#[diagnostic::on_unimplemented(
message = "all values of `{Self}` are not known statically",
label = "not exhaustive",
note = "consider annotating `{Self}` with `#[derive(Exhaustive)]`"
)]
pub unsafe trait Exhaustive: Sized + Copy {
type Num: ArrayLength<ArrayType<Self>: Copy>;
const ALL: GenericArray<Self, Self::Num>;
}
unsafe impl Exhaustive for Infallible {
type Num = U0;
const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([]);
}
unsafe impl Exhaustive for () {
type Num = U1;
const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([()]);
}
unsafe impl Exhaustive for PhantomPinned {
type Num = U1;
const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([Self]);
}
unsafe impl<T: ?Sized> Exhaustive for PhantomData<T> {
type Num = U1;
const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([Self]);
}
unsafe impl Exhaustive for bool {
type Num = U2;
const ALL: GenericArray<Self, Self::Num> = GenericArray::from_array([false, true]);
}
unsafe impl<T: Exhaustive> Exhaustive for Option<T>
where
U1: Add<T::Num, Output: ArrayLength<ArrayType<Self>: Copy>>,
{
type Num = Sum<U1, T::Num>;
const ALL: GenericArray<Self, Self::Num> =
concat::<_, U1, T::Num>(GenericArray::from_array([None]), map!(T::ALL, |t| Some(t)));
}
unsafe impl<T: Exhaustive, E: Exhaustive> Exhaustive for Result<T, E>
where
T::Num: Add<E::Num, Output: ArrayLength<ArrayType<Self>: Copy>>,
{
type Num = Sum<T::Num, E::Num>;
const ALL: GenericArray<Self, Self::Num> = concat::<_, T::Num, E::Num>(
map!(T::ALL, |t| Ok::<T, E>(t)),
map!(E::ALL, |t| Err::<T, E>(t)),
);
}
unsafe impl<T: Exhaustive, const N: usize> Exhaustive for [T; N]
where
Const<N>: ToUInt<Output: ArrayLength>,
<T::Num as ArrayLength>::ArrayType<usize>: ConstDefault,
T::Num: Pow<U<N>, Output: ArrayLength<ArrayType<Self>: Copy>>,
{
type Num = <T::Num as Pow<U<N>>>::Output;
const ALL: GenericArray<Self, Self::Num> = from_fn!(Self::Num, |i| {
let perm: GenericArray<T, U<N>> = from_fn!(U<N>, |j| {
#[expect(
clippy::cast_possible_truncation,
reason = "we have no other way to cast in a const context"
)]
let index = (i / T::Num::USIZE.pow(N as u32 - j as u32 - 1)) % T::Num::USIZE;
T::ALL.as_slice()[index]
});
perm
});
}
type ProdAll<T> = <T as MulAll>::Output;
#[doc(hidden)]
pub trait MulAll {
type Output: ArrayLength;
}
impl MulAll for () {
type Output = U1;
}
impl<T: ArrayLength> MulAll for (T,) {
type Output = T;
}
macro_rules! impl_variadic {
($(#[$meta:meta])* $(($T:ident, $t:ident)),*) => {
impl<$($T,)* Last> MulAll for ($($T,)* Last,)
where
($($T,)*): MulAll,
Last: Mul<<($($T,)*) as MulAll>::Output, Output: ArrayLength>,
{
type Output = <Last as Mul<<($($T,)*) as MulAll>::Output>>::Output;
}
$(#[$meta])*
unsafe impl<$($T: Exhaustive,)*> Exhaustive for ($($T,)*)
where
($($T::Num,)*): MulAll,
<ProdAll<($($T::Num,)*)> as ArrayLength>::ArrayType<Self>: Copy,
{
type Num = ProdAll<($($T::Num,)*)>;
const ALL: GenericArray<Self, Self::Num> = {
let mut all: GenericArray<MaybeUninit<Self>, Self::Num> =
unsafe { MaybeUninit::uninit().assume_init() };
let mut i = 0;
while i < <ProdAll<($($T::Num,)*)>>::USIZE {
let [$($t,)*] = split_index(i, [$($T::Num::USIZE,)*]);
let tuple = ($($T::ALL.as_slice()[$t],)*);
all.as_mut_slice()[i] = MaybeUninit::new(tuple);
i += 1;
}
unsafe { const_transmute(all) }
};
}
};
}
all_tuples!(
#[doc(fake_variadic)]
impl_variadic,
1,
16,
T,
t
);
const fn split_index<const N: usize>(mut index: usize, lengths: [usize; N]) -> [usize; N] {
let mut result = [0; N];
let mut i = 0;
while i < N {
result[N - 1 - i] = index % lengths[N - 1 - i];
index /= lengths[N - 1 - i];
i += 1;
}
result
}