struct_to_enum/lib.rs
1#[cfg(feature = "derive")]
2pub use struct_to_enum_macros::*;
3
4/// Implemented by structs that have a `FieldName` enum generated by the [`FieldName`] derive macro.
5///
6/// `N` is the number of non-skipped fields + nested fields, resolved at compile time. Deriving `FieldName` on a
7/// struct `Foo` with `N` non-skipped fields generates:
8///
9/// ```ignore
10/// impl FieldNames<N> for Foo {
11/// type FieldName = FooFieldName;
12/// fn field_names() -> [FooFieldName; N] { ... }
13/// }
14/// ```
15///
16/// Call [`field_names`](FieldNames::field_names) to get an ordered array of all field-name
17/// variants without needing a struct instance.
18/// Ordered by field declaration order.
19///
20/// # Example
21///
22/// ```rust
23/// use struct_to_enum::{FieldName, FieldNames};
24///
25/// #[derive(FieldName)]
26/// struct Point {
27/// x: f32,
28/// y: f32,
29/// }
30///
31/// let names: [PointFieldName; 2] = Point::field_names();
32/// assert_eq!(names, [PointFieldName::X, PointFieldName::Y]);
33/// ```
34pub trait FieldNames<const N: usize> {
35 type FieldName;
36
37 // can't use const generics in trait bounds yet (requires nightly)
38 // #![feature(generic_const_exprs)]
39 // const N: usize;
40
41 /// Returns all field-name enum variants in field declaration order.
42 ///
43 /// NOTE: This method will be made `const` once `const_trait_impl` is stabilised.
44 /// Tracking issue: <https://github.com/rust-lang/rust/issues/67792>
45 fn field_names() -> [Self::FieldName; N];
46}
47
48#[cfg(test)]
49mod tests {
50 use crate::FieldNames;
51
52 #[derive(Debug, PartialEq, Clone, Copy)]
53 pub struct Test {
54 pub a: i32,
55 pub b: i32,
56 }
57
58 #[derive(Debug, PartialEq, Clone, Copy)]
59 pub enum TestFieldName {
60 A,
61 B,
62 }
63
64 impl FieldNames<2> for Test {
65 type FieldName = TestFieldName;
66 fn field_names() -> [Self::FieldName; 2] {
67 [TestFieldName::A, TestFieldName::B]
68 }
69 }
70
71 #[test]
72 pub fn test_field_names() {
73 let _test = Test { a: 0, b: 0 };
74 let _a: TestFieldName = <Test as FieldNames<2>>::FieldName::A;
75 assert_eq!(Test::field_names(), [TestFieldName::A, TestFieldName::B]);
76 }
77}