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}