const-exhaustive 1.0.0

Enumerate all values of a type at compile time
Documentation
#![expect(missing_docs, reason = "test module")]

use {
    const_exhaustive::Exhaustive,
    core::{fmt::Debug, marker::PhantomData},
};

fn assert_all<T: Exhaustive + Debug + PartialEq>(values: impl IntoIterator<Item = T>) {
    let values = values.into_iter().collect::<Vec<_>>();
    assert_eq!(values.as_slice(), T::ALL.as_slice());
}

#[test]
fn unit_struct() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Dummy;

    assert_all([Dummy]);
}

#[test]
fn tuple_struct_unit_single() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Dummy(());

    assert_all([Dummy(())]);
}

#[test]
fn tuple_struct_unit_many() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Dummy((), (), ());

    assert_all([Dummy((), (), ())]);
}

#[test]
fn tuple_struct_bool_single() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Dummy(bool);

    assert_all([Dummy(false), Dummy(true)]);
}

#[test]
fn tuple_struct_bool_many() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Dummy(bool, bool, bool);

    assert_all([
        Dummy(false, false, false),
        Dummy(false, false, true),
        Dummy(false, true, false),
        Dummy(false, true, true),
        Dummy(true, false, false),
        Dummy(true, false, true),
        Dummy(true, true, false),
        Dummy(true, true, true),
    ]);
}

#[test]
fn named_field_struct_empty() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Dummy {}

    assert_all([Dummy {}]);
}

#[test]
fn named_field_struct_small() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Dummy {
        a: bool,
    }

    assert_all([Dummy { a: false }, Dummy { a: true }]);
}

#[test]
fn named_field_struct_large() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Dummy {
        a: (),
        b: bool,
        c: bool,
    }

    assert_all([
        Dummy {
            a: (),
            b: false,
            c: false,
        },
        Dummy {
            a: (),
            b: false,
            c: true,
        },
        Dummy {
            a: (),
            b: true,
            c: false,
        },
        Dummy {
            a: (),
            b: true,
            c: true,
        },
    ]);
}

#[test]
fn enum_uninhabited() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum Dummy {}

    assert_all::<Dummy>([]);
}

#[test]
fn enum_unit_single() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum Dummy {
        A,
    }

    assert_eq!([Dummy::A], Dummy::ALL.as_slice());
}

#[test]
fn enum_unit_many() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum Dummy {
        A,
        B,
        C,
    }

    assert_eq!([Dummy::A, Dummy::B, Dummy::C], Dummy::ALL.as_slice());
}

#[test]
fn enum_tuple_variants() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum Dummy {
        A(),
        B(()),
        C(bool, bool),
    }

    assert_eq!(
        [
            Dummy::A(),
            Dummy::B(()),
            Dummy::C(false, false),
            Dummy::C(false, true),
            Dummy::C(true, false),
            Dummy::C(true, true),
        ],
        Dummy::ALL.as_slice()
    );
}

#[test]
fn enum_named_field_variants() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum Dummy {
        A {},
        B { foo: () },
        C { foo: bool, bar: bool },
    }

    assert_eq!(
        [
            Dummy::A {},
            Dummy::B { foo: () },
            Dummy::C {
                foo: false,
                bar: false,
            },
            Dummy::C {
                foo: false,
                bar: true,
            },
            Dummy::C {
                foo: true,
                bar: false,
            },
            Dummy::C {
                foo: true,
                bar: true,
            },
        ],
        Dummy::ALL.as_slice()
    );
}

#[test]
fn enum_variant_mix() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum Dummy {
        Unit,
        Tuple(bool),
        Named { foo: bool },
        EndUnit,
    }

    assert_eq!(
        [
            Dummy::Unit,
            Dummy::Tuple(false),
            Dummy::Tuple(true),
            Dummy::Named { foo: false },
            Dummy::Named { foo: true },
            Dummy::EndUnit,
        ],
        Dummy::ALL.as_slice()
    );
}

#[test]
fn compound() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct BoolWrapper(bool);

    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum Compound {
        A(bool),
        B(BoolWrapper),
    }

    assert_eq!(
        [
            Compound::A(false),
            Compound::A(true),
            Compound::B(BoolWrapper(false)),
            Compound::B(BoolWrapper(true)),
        ],
        Compound::ALL.as_slice()
    );
}

#[test]
#[expect(clippy::items_after_statements, reason = "easier to read")]
fn generic() {
    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Wrapper1<T>(T);
    assert_eq!(Wrapper1::<()>::ALL.as_slice(), [Wrapper1(())]);
    assert_eq!(
        Wrapper1::<bool>::ALL.as_slice(),
        [Wrapper1(false), Wrapper1(true)],
    );

    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct Wrapper2<A, B>(A, B);
    assert_eq!(Wrapper2::<(), ()>::ALL.as_slice(), [Wrapper2((), ())]);
    assert_eq!(
        Wrapper2::<(), bool>::ALL.as_slice(),
        [Wrapper2((), false), Wrapper2((), true)],
    );
    assert_eq!(
        Wrapper2::<bool, bool>::ALL.as_slice(),
        [
            Wrapper2(false, false),
            Wrapper2(false, true),
            Wrapper2(true, false),
            Wrapper2(true, true)
        ],
    );

    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct WrapperNamed<A, B> {
        a: A,
        b: B,
    }
    assert_eq!(
        WrapperNamed::<(), ()>::ALL.as_slice(),
        [WrapperNamed { a: (), b: () }]
    );
    assert_eq!(
        WrapperNamed::<(), bool>::ALL.as_slice(),
        [
            WrapperNamed { a: (), b: false },
            WrapperNamed { a: (), b: true }
        ]
    );
    assert_eq!(
        WrapperNamed::<bool, bool>::ALL.as_slice(),
        [
            WrapperNamed { a: false, b: false },
            WrapperNamed { a: false, b: true },
            WrapperNamed { a: true, b: false },
            WrapperNamed { a: true, b: true }
        ],
    );

    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct WrapperOption<A, B> {
        a: A,
        b: Option<B>,
    }
    assert_eq!(
        WrapperOption::<(), ()>::ALL.as_slice(),
        [
            WrapperOption { a: (), b: None },
            WrapperOption { a: (), b: Some(()) }
        ]
    );
    assert_eq!(
        WrapperOption::<(), bool>::ALL.as_slice(),
        [
            WrapperOption { a: (), b: None },
            WrapperOption {
                a: (),
                b: Some(false)
            },
            WrapperOption {
                a: (),
                b: Some(true)
            },
        ]
    );
    assert_eq!(
        WrapperOption::<bool, bool>::ALL.as_slice(),
        [
            WrapperOption { a: false, b: None },
            WrapperOption {
                a: false,
                b: Some(false)
            },
            WrapperOption {
                a: false,
                b: Some(true)
            },
            WrapperOption { a: true, b: None },
            WrapperOption {
                a: true,
                b: Some(false)
            },
            WrapperOption {
                a: true,
                b: Some(true)
            }
        ],
    );

    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    struct WrapperPhantom<T> {
        _phantom: PhantomData<T>,
    }
    assert_eq!(
        WrapperPhantom::<()>::ALL.as_slice(),
        [WrapperPhantom {
            _phantom: PhantomData
        }],
    );
    assert_eq!(
        WrapperPhantom::<bool>::ALL.as_slice(),
        [WrapperPhantom {
            _phantom: PhantomData
        }],
    );

    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum WrapperEnum1<T> {
        T(T),
    }
    assert_eq!(WrapperEnum1::<()>::ALL.as_slice(), [WrapperEnum1::T(())]);
    assert_eq!(
        WrapperEnum1::<bool>::ALL.as_slice(),
        [WrapperEnum1::T(false), WrapperEnum1::T(true)]
    );

    #[derive(Debug, Clone, Copy, PartialEq, Exhaustive)]
    enum WrapperEnum2<T> {
        A { a: T },
        B(T),
        C((T, T)),
        D,
    }
    assert_eq!(
        WrapperEnum2::<()>::ALL.as_slice(),
        [
            WrapperEnum2::A { a: () },
            WrapperEnum2::B(()),
            WrapperEnum2::C(((), ())),
            WrapperEnum2::D
        ]
    );
}