struct-to-enum 1.0.0

Trait definitions for struct-to-enum derive macros
Documentation
  • Coverage
  • 50%
    2 out of 4 items documented1 out of 2 items with examples
  • Size
  • Source code size: 11.18 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 1.05 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 15s Average build duration of successful builds.
  • all releases: 10s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • sinder38

struct-to-enum

Derive macros for generating enums corresponding to the fields of structs.

Overview

struct-to-enum provides two derive macros:

  • FieldName — generates a unit enum where each variant represents a field name
  • FieldType — 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.

Usage

FieldName

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]);

Nested flattening

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]);

FieldType

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));

Nested flattening

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));

Generics

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));

Combining both macros

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();

Attributes

FieldName attributes

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

FieldType attributes

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_name and stem_type / ste_type are interchangeable.

Example: using with strum

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"]
);

Inspired by:

Strum Field Types

License

Licensed under either of

at your option