impl_new_derive/
lib.rs

1//! # impl_new_derive
2//!
3//! `ImplNew` is a Rust procedural macro that automatically generates a constructor (`new` method)
4//! for structs with named fields. It initializes public fields from provided arguments, and private
5//! fields are automatically initialized using `Default::default()`.
6//!
7//! ## Features
8//! - **Automatic constructor generation**: Generates a `new` method for structs.
9//! - **Public fields**: Public fields are passed as parameters to the `new` method.
10//! - **Private fields**: Private fields are initialized with `Default::default()`.
11//! - **Generics support**: The macro works for both generic and non-generic structs.
12//!
13//! ## Usage
14//!
15//! Add the following dependency to your `Cargo.toml` file to use the macro:
16//!
17//! ```toml
18//! [dependencies]
19//! impl_new_derive = "0.1.0"
20//! ```
21//!
22//! Then, annotate your struct with `#[derive(ImplNew)]` to generate the `new` method.
23//!
24//! ### Example for a Non-Generic Struct
25//!
26//! ```rust
27//! use impl_new_derive::ImplNew;
28//!
29//! #[derive(ImplNew, Default)]
30//! struct MyStruct {
31//!     pub name: String,
32//!     pub age: u32,
33//!     secret: String, // This field is private
34//! }
35//!
36//! fn main() {
37//!     let my_struct = MyStruct::new("John".to_string(), 30);
38//!     println!("Name: {}, Age: {}", my_struct.name, my_struct.age);
39//! }
40//! ```
41//!
42//! In this example:
43//! - `name` and `age` are public fields and are passed as arguments to the `new` function.
44//! - `secret` is a private field and is automatically initialized to its default value.
45//!
46//! ### Example for a Generic Struct
47//!
48//! ```rust
49//! use impl_new_derive::ImplNew;
50//!
51//! #[derive(ImplNew, Default)]
52//! struct MyStruct<T> {
53//!     pub value: T,
54//!     count: usize, // This field is private
55//! }
56//!
57//! fn main() {
58//!     let my_struct = MyStruct::new(42);
59//!     println!("Value: {}", my_struct.value);
60//! }
61//! ```
62//!
63//! ## How It Works
64//!
65//! When the `ImplNew` macro is applied to a struct, the macro performs the following actions:
66//! - Iterates over the struct's fields.
67//! - Public fields are added as parameters to the generated `new` function.
68//! - Non-public fields are initialized with `Default::default()`.
69//! - If the struct contains generics, the macro correctly handles them in the `impl` block.
70//!
71//! ## Limitations
72//! - The `ImplNew` macro only works for structs with named fields.
73//! - Private fields must implement `Default`, or the macro will fail to compile.
74//!
75//! ## License
76//!
77//! Licensed under the MIT License.
78extern crate proc_macro;
79
80use proc_macro::TokenStream;
81use proc_macro2::TokenStream as TokenStream2;
82use quote::quote;
83use syn::{parse_macro_input, Attribute, DeriveInput, Expr, FieldsNamed, Visibility};
84
85#[proc_macro_derive(ImplNew, attributes(default))]
86pub fn derive_impl_new(input: TokenStream) -> TokenStream {
87    // Parse the input tokens into a syntax tree
88    let input = parse_macro_input!(input as DeriveInput);
89    let name = input.ident;
90    let generics = input.generics;
91    let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
92
93    // Only support named structs
94    let fields = if let syn::Data::Struct(data) = input.data {
95        if let syn::Fields::Named(FieldsNamed { named, .. }) = data.fields {
96            named
97        } else {
98            panic!("`ImplNew` macro can only be used on structs with named fields");
99        }
100    } else {
101        panic!("`ImplNew` macro can only be used on structs");
102    };
103
104    // Separate public vs. non-public fields
105    let pub_fields = fields
106        .iter()
107        .filter(|f| matches!(f.vis, Visibility::Public(_)))
108        .collect::<Vec<_>>();
109    let non_pub_fields = fields
110        .iter()
111        .filter(|f| !matches!(f.vis, Visibility::Public(_)))
112        .collect::<Vec<_>>();
113
114    // Collect public field names and types
115    let pub_field_names = pub_fields.iter().map(|f| &f.ident).collect::<Vec<_>>();
116    let pub_field_types = pub_fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
117
118    // Initialize non-public fields
119    let non_pub_field_initializations = non_pub_fields.iter().map(|f| {
120        let field_name = &f.ident;
121        // If a #[default(expr)] attribute is present, use that expr
122        if let Some(default_expr) = extract_default_value(&f.attrs) {
123            quote! { #field_name: #default_expr }
124        } else {
125            // Otherwise default to Default::default()
126            quote! { #field_name: Default::default() }
127        }
128    });
129
130    // Build the implementation
131    let expanded = quote! {
132        impl #impl_generics #name #ty_generics #where_clause {
133            #[must_use]
134            pub fn new(#(#pub_field_names: #pub_field_types),*) -> Self {
135                Self {
136                    // Public fields come from the constructor params
137                    #(#pub_field_names),*,
138                    // Non-public fields automatically initialized
139                    #(#non_pub_field_initializations),*
140                }
141            }
142        }
143    };
144
145    // Return the generated impl
146    TokenStream::from(expanded)
147}
148
149// Extract the expression specified in #[default(...)]
150fn extract_default_value(attrs: &[Attribute]) -> Option<TokenStream2> {
151    for attr in attrs {
152        if attr.path().is_ident("default") {
153            // parse_args::<Expr>() interprets the attribute's tokens as an expression
154            if let Ok(expr) = attr.parse_args::<Expr>() {
155                return Some(quote! { #expr });
156            }
157        }
158    }
159    None
160}