struct-to-enum crate provides two derive macros for generating enums corresponding to the fields of structs:
FieldName— generates a unit enum where each variant represents a field nameFieldType— generates an enum where each variant wraps the type of a struct field
Both macros support generics, field skipping, nesting, and custom derives/attributes on the generated enum.
Intended use-case is to generate enums from struct fields, enabling compile-time column filter validation for queries.
[dependencies]
struct-to-enum = {version="1.0", features = ["derive"] }Generates a unit enum {StructName}FieldName and implements [FieldNames<N>] on the struct,
which provides a field_names() static method returning an ordered array of all variants.
use struct_to_enum::{FieldName, FieldNames};
#[derive(FieldName)]
struct Point {
x: f32,
y: f32,
#[stem_name(skip)]
label: String,
}
// Generated:
// #[derive(Debug, PartialEq, Eq, Clone, Copy)]
// enum PointFieldName { X, Y }
let names: [PointFieldName; 2] = Point::field_names();
assert_eq!(names, [PointFieldName::X, PointFieldName::Y]);Mark a field with #[stem_name(nested)] to flatten its FieldName variants into the parent enum.
The nested struct must also derive FieldName. Nesting can be arbitrarily deep.
use struct_to_enum::{FieldName, FieldNames};
#[derive(FieldName)]
pub struct Address {
pub street: String,
pub city: String,
}
#[derive(FieldName)]
struct Person {
name: String,
#[stem_name(nested)]
address: Address,
}
// PersonFieldName has variants: Name, Street, City (Address's variants are inlined)
let names: [PersonFieldName; 3] = Person::field_names();
assert_eq!(names, [PersonFieldName::Name, PersonFieldName::Street, PersonFieldName::City]);Generates a tuple-variant enum {StructName}FieldType and implements From<Struct> for
[FieldType; N], converting the struct into an ordered array of variants holding the field values.
use struct_to_enum::FieldType;
#[derive(FieldType)]
#[stem_type_derive(Debug, Clone, PartialEq)]
struct Config {
width: u32,
height: u32,
#[stem_type(skip)]
name: String,
}
// Generated:
// #[derive(Debug, Clone, PartialEq)]
// enum ConfigFieldType {
// Width(u32),
// Height(u32),
// }
let cfg = Config { width: 1920, height: 1080, name: "hd".into() };
let fields: [ConfigFieldType; 2] = cfg.into();
assert_eq!(fields[0], ConfigFieldType::Width(1920));
assert_eq!(fields[1], ConfigFieldType::Height(1080));Mark a field with #[stem_type(nested)] to flatten the nested struct's FieldType variants
into the parent enum. The nested struct must also derive FieldType. Nesting can be arbitrarily deep.
use struct_to_enum::FieldType;
#[derive(FieldType)]
#[stem_type_derive(Debug, PartialEq)]
struct Color {
r: u8,
g: u8,
b: u8,
}
#[derive(FieldType)]
#[stem_type_derive(Debug, PartialEq)]
struct Pixel {
x: i32,
y: i32,
#[stem_type(nested)]
color: Color,
}
// PixelFieldType has variants: X(i32), Y(i32), R(u8), G(u8), B(u8)
let p = Pixel { x: 10, y: 20, color: Color { r: 255, g: 128, b: 0 } };
let fields: [PixelFieldType; 5] = p.into();
assert_eq!(fields[0], PixelFieldType::X(10));
assert_eq!(fields[2], PixelFieldType::R(255));Both macros handle generic structs with lifetime and type parameters.
use struct_to_enum::FieldType;
#[derive(FieldType)]
#[stem_type_derive(Debug, PartialEq)]
struct Pair<A, B> {
first: A,
second: B,
}
let fields: [PairFieldType<i32, &str>; 2] = Pair { first: 1_i32, second: "hello" }.into();
assert_eq!(fields[0], PairFieldType::First(1));use struct_to_enum::{FieldName, FieldNames, FieldType};
#[derive(FieldName, FieldType)]
#[stem_type_derive(Debug)]
struct Record {
id: u64,
value: f64,
}
// RecordFieldName and RecordFieldType are both generated
let _names: [RecordFieldName; 2] = Record::field_names();| Attribute | Location | Description |
|---|---|---|
#[stem_name_derive(...)] |
Struct | Merge additional derives onto the generated enum (defaults Debug, PartialEq, Eq, Clone, Copy are always kept) |
#[stem_name_attr(...)] |
Struct | Add extra attributes to the generated enum |
#[stem_name(skip)] |
Field | Exclude this field from the generated enum |
#[stem_name(nested)] |
Field | Flatten the field's FieldName variants into the parent enum |
| Attribute | Location | Description |
|---|---|---|
#[stem_type_derive(...)] |
Struct | Specify derives for the generated enum (none by default) |
#[stem_type_attr(...)] |
Struct | Add extra attributes to the generated enum |
#[stem_type(skip)] |
Field | Exclude this field from the generated enum |
#[stem_type(nested)] |
Field | Flatten the field's FieldType variants into the parent enum |
Attribute aliases:
stem_name/ste_nameandstem_type/ste_typeare interchangeable.
use struct_to_enum::FieldType;
use strum::VariantNames;
#[derive(FieldType)]
#[stem_type_derive(Debug, strum_macros::VariantNames)]
#[stem_type_attr(strum(serialize_all = "SCREAMING-KEBAB-CASE"))]
struct Request {
user_id: u64,
payload: Vec<u8>,
}
assert_eq!(
RequestFieldType::VARIANTS,
&["USER-ID", "PAYLOAD"]
);- field_types - the original inspiration for this crate.
struct-to-enumextends the idea with nesting, derive attributes, generics, and more. - strum - solves a similar problem (enum utilities via derive macros); a useful companion crate and a reference during development.
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in struct-to-enum by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.