1pub use assert_order_derive::VariantOrder;
27
28pub trait VariantOrder {
31 fn order() -> &'static [&'static str];
34}
35
36pub fn assert_order<E, O, V>(order: O)
43where
44 E: VariantOrder,
45 O: IntoIterator<Item = V>,
46 V: AsRef<str>,
47{
48 let variants = E::order();
49 let iter = order.into_iter();
50
51 let mut n: usize = 0;
52 for variant in iter {
53 let variant = variant.as_ref();
54 let canonical = variants.get(n).map(|canonical| *canonical);
55
56 let Some(canonical) = canonical else {
57 panic!("Expected more canonical variants.");
58 };
59
60 assert!(
61 variant == canonical,
62 "Variant name mismatch: Expected \"{}\", got \"{}\"",
63 variant,
64 canonical,
65 );
66
67 n += 1;
68 }
69
70 assert!(
71 variants.len() == n,
72 "unexpected length: expected {}, got {}",
73 n,
74 variants.len()
75 );
76}
77
78#[cfg(test)]
79mod tests {
80 use std::thread::spawn;
81
82 use crate::{VariantOrder, assert_order};
83
84 #[test]
85 fn assert_proper_ordering() {
86 #[derive(VariantOrder)]
87 #[expect(unused)]
88 enum TestEnum {
89 A,
90 B(),
91 C {},
92 }
93
94 assert_eq!(["A", "B", "C"], TestEnum::order());
95 }
96
97 #[test]
98 fn expect_nonpanic() {
99 #[derive(VariantOrder)]
100 #[expect(unused)]
101 enum TestEnum {
102 A,
103 B(),
104 C {},
105 }
106
107 assert_order::<TestEnum, _, _>(["A", "B", "C"]);
108 }
109
110 #[test]
111 fn expect_order_panic() {
112 let thread = spawn(|| {
113 #[derive(VariantOrder)]
114 #[expect(unused)]
115 enum TestEnum {
116 A,
117 B(),
118 C {},
119 }
120
121 assert_order::<TestEnum, _, _>(["A", "C", "B"]);
122 });
123
124 let panic = thread.join().expect_err("expected panic");
125 let panic = panic
126 .downcast_ref::<String>()
127 .expect("panic as string")
128 .as_str();
129
130 assert_eq!(panic, "Variant name mismatch: Expected \"C\", got \"B\"");
131 }
132
133 #[test]
134 fn expect_too_long_panic() {
135 let thread = spawn(|| {
136 #[derive(VariantOrder)]
137 #[expect(unused)]
138 enum TestEnum {
139 A,
140 B(),
141 C {},
142 }
143
144 assert_order::<TestEnum, _, _>(["A", "B"]);
145 });
146
147 let panic = thread.join().expect_err("expected panic");
148 let panic = panic
149 .downcast_ref::<String>()
150 .expect("panic as string")
151 .as_str();
152
153 assert_eq!(panic, "unexpected length: expected 2, got 3");
154 }
155
156 #[test]
157 fn expect_too_short_panic() {
158 let thread = spawn(|| {
159 #[derive(VariantOrder)]
160 #[expect(unused)]
161 enum TestEnum {
162 A,
163 B(),
164 C {},
165 }
166
167 assert_order::<TestEnum, _, _>(["A", "B", "C", "D"]);
168 });
169
170 let panic = thread.join().expect_err("expected panic");
171 let panic = *(panic
172 .downcast_ref::<&'static str>()
173 .expect("panic as string"));
174
175 assert_eq!(panic, "Expected more canonical variants.");
176 }
177}