pub use assert_order_derive::VariantOrder;
pub trait VariantOrder {
fn order() -> &'static [&'static str];
}
pub fn assert_order<E, O, V>(order: O)
where
E: VariantOrder,
O: IntoIterator<Item = V>,
V: AsRef<str>,
{
let variants = E::order();
let iter = order.into_iter();
let mut n: usize = 0;
for variant in iter {
let variant = variant.as_ref();
let canonical = variants.get(n).map(|canonical| *canonical);
let Some(canonical) = canonical else {
panic!("Expected more canonical variants.");
};
assert!(
variant == canonical,
"Variant name mismatch: Expected \"{}\", got \"{}\"",
variant,
canonical,
);
n += 1;
}
assert!(
variants.len() == n,
"unexpected length: expected {}, got {}",
n,
variants.len()
);
}
#[cfg(test)]
mod tests {
use std::thread::spawn;
use crate::{VariantOrder, assert_order};
#[test]
fn assert_proper_ordering() {
#[derive(VariantOrder)]
#[expect(unused)]
enum TestEnum {
A,
B(),
C {},
}
assert_eq!(["A", "B", "C"], TestEnum::order());
}
#[test]
fn expect_nonpanic() {
#[derive(VariantOrder)]
#[expect(unused)]
enum TestEnum {
A,
B(),
C {},
}
assert_order::<TestEnum, _, _>(["A", "B", "C"]);
}
#[test]
fn expect_order_panic() {
let thread = spawn(|| {
#[derive(VariantOrder)]
#[expect(unused)]
enum TestEnum {
A,
B(),
C {},
}
assert_order::<TestEnum, _, _>(["A", "C", "B"]);
});
let panic = thread.join().expect_err("expected panic");
let panic = panic
.downcast_ref::<String>()
.expect("panic as string")
.as_str();
assert_eq!(panic, "Variant name mismatch: Expected \"C\", got \"B\"");
}
#[test]
fn expect_too_long_panic() {
let thread = spawn(|| {
#[derive(VariantOrder)]
#[expect(unused)]
enum TestEnum {
A,
B(),
C {},
}
assert_order::<TestEnum, _, _>(["A", "B"]);
});
let panic = thread.join().expect_err("expected panic");
let panic = panic
.downcast_ref::<String>()
.expect("panic as string")
.as_str();
assert_eq!(panic, "unexpected length: expected 2, got 3");
}
#[test]
fn expect_too_short_panic() {
let thread = spawn(|| {
#[derive(VariantOrder)]
#[expect(unused)]
enum TestEnum {
A,
B(),
C {},
}
assert_order::<TestEnum, _, _>(["A", "B", "C", "D"]);
});
let panic = thread.join().expect_err("expected panic");
let panic = *(panic
.downcast_ref::<&'static str>()
.expect("panic as string"));
assert_eq!(panic, "Expected more canonical variants.");
}
}