variants_data_struct/
lib.rs

1#[doc = include_str!("../README.md")]
2use proc_macro::TokenStream;
3
4mod variants_data_struct_attr_meta;
5mod variants_data_struct_defs;
6mod variants_data_struct_field_attr_meta;
7mod variants_data_struct_field_meta;
8mod variants_data_struct_meta;
9
10use crate::variants_data_struct_attr_meta::VariantsDataStructAttrMeta;
11use crate::variants_data_struct_defs::{VariantsDataStructDefs, variants_data_struct_defs};
12use crate::variants_data_struct_meta::VariantsDataStructMeta;
13
14/// Derive macro to generate a data struct containing fields for each variant of the enum.
15///
16/// ```rust
17/// use variants_data_struct::VariantsDataStruct;
18///
19/// #[derive(VariantsDataStruct)]
20/// pub enum MyEnum {
21///     UnitEnum,
22///     TupleEnum(i32, String),
23///     StructEnum { id: u32, name: String },
24/// }
25///
26/// // Equivalent to:
27/// // pub struct MyEnumVariantsData {
28/// //     pub unit_enum: (),
29/// //     pub tuple_enum: TupleEnumVariantType,
30/// //     pub struct_enum: StructEnumVariantType,
31/// // }
32/// //
33/// // pub struct TupleEnumVariantType(pub i32, pub String);
34/// //
35/// // pub struct StructEnumVariantType {
36/// //     pub id: u32,
37/// //     pub name: String,
38/// // }
39/// ```
40///
41/// ## Helper attributes
42///
43/// ### `#[variants_data_struct(<meta>)]` customizes the behavior of the derive macro.
44///
45/// The `<meta>` (see [`VariantsDataStructAttrMeta`](crate::variants_data_struct_attr_meta::VariantsDataStructAttrMeta))
46/// is a comma-separated list that can contain the following items:
47///
48// - `attrs(#[derive(...)] ...)`: Adds the specified attributes to the generated data struct. Notably, you
49/// can use it to add derives like `Debug`, `Clone` to the generated struct.
50/// - `vis = <visibility>`: Specifies a custom visibility for the generated data struct. If not provided,
51/// the visibility of the original enum is used.
52/// - `name = <CustomName>`: Specifies a custom name for the generated data struct.
53///  If not provided, the default name is `<EnumName>VariantsData`.
54/// - `variants_tys_attrs(#[derive(...)] ...)`: Adds the specified attributes to each of the generated variant type structs.
55/// Notably, you can use it to add derives like `Debug`, `Clone` to the generated variant type structs.
56///
57/// ### `#[variants_data_struct_field(<meta>)]` customizes the behavior of individual fields in the generated data struct
58/// and their corresponding variant types.
59///
60/// The `<meta>` (see [`VariantsDataStructFieldAttrMeta`](crate::variants_data_struct_field_attr_meta::VariantsDataStructFieldAttrMeta))
61/// is a comma-separated list that can contain the following items:
62///
63/// - `field_attrs(#[derive(...)] ...)`: Adds the specified attributes to the generated field in the data struct.
64/// Notably, you can use it to add derives like `Debug`, `Clone` to
65/// the generated field.
66/// - `field_vis = <visibility>`: Specifies a custom visibility for the generated field in the data struct. If not provided,
67/// the visibility of the generated data struct is used.
68/// - `field_name = <custom_field_name>`: Specifies a custom name for the generated field in the data struct. If not provided,
69/// the name is derived from the original variant's name (converted to `snake_case`).
70/// - `field_ty_override`: Overrides the type of the generated field in the data struct. If not provided,
71/// the type is derived from the original variant's fields. For variants without fields (a unit variant or a struct or tuple variant with no fields),
72/// the type is `()`. For tuple and struct variants, a separate "variant type" struct is generated to encapsulate the fields.
73/// - `gen_variant_ty`: Overrides the decision whether to generate a separate "variant type" struct for the variant.
74/// If not provided, a "variant type" struct is generated for tuple and struct variants, and not for unit variants.
75#[proc_macro_derive(
76    VariantsDataStruct,
77    attributes(variants_data_struct, variants_data_struct_field)
78)]
79pub fn derive_variants_data_struct(item: TokenStream) -> TokenStream {
80    let input = syn::parse_macro_input!(item as syn::DeriveInput);
81    let syn::DeriveInput {
82        attrs,
83        vis,
84        ident,
85        generics,
86        data,
87    } = input;
88    let syn::Data::Enum(enum_data) = data else {
89        return syn::Error::new_spanned(
90            ident,
91            concat!(
92                stringify!(VariantsDataStruct),
93                " can only be derived for enums"
94            ),
95        )
96        .to_compile_error()
97        .into();
98    };
99
100    // Parse the `variants_data_struct` attribute meta
101    let variants_data_struct_attr_meta: VariantsDataStructAttrMeta =
102        match VariantsDataStructAttrMeta::from_attrs(attrs).map(Option::unwrap_or_default) {
103            Ok(meta) => meta,
104            Err(err) => return err.to_compile_error().into(),
105        };
106
107    // Resolve the final metadata for the derived variants data struct
108    let VariantsDataStructMeta {
109        attrs: variants_data_struct_attrs,
110        vis: variants_data_struct_vis,
111        name: variants_data_struct_name,
112        variants_tys_attrs,
113    } = VariantsDataStructMeta::resolve(variants_data_struct_attr_meta, &ident, &vis);
114
115    // Generate the variants data struct definitions
116    let VariantsDataStructDefs {
117        derived_struct,
118        variant_type_structs,
119    } = match variants_data_struct_defs(
120        variants_data_struct_attrs,
121        variants_tys_attrs,
122        variants_data_struct_vis,
123        variants_data_struct_name,
124        generics,
125        enum_data.variants,
126    ) {
127        Ok(defs) => defs,
128        Err(err) => return err.to_compile_error().into(),
129    };
130
131    quote::quote! {
132        #derived_struct
133
134        #(#variant_type_structs)*
135    }
136    .into()
137}