1use darling::{ast::Data, util::Flag, Error, FromDeriveInput, FromField};
46use proc_macro2::Span;
47use quote::quote;
48use syn::{parse_macro_input, GenericParam, Generics, Ident, Path, Type, Visibility};
49
50#[derive(Debug, FromField)]
51#[darling(attributes(constructor), and_then = "Self::not_both")]
52struct Field {
53 ident: Option<Ident>,
54 ty: Type,
55
56 required: Flag,
57 default: Flag,
58}
59impl Field {
60 fn not_both(self) -> darling::Result<Self> {
61 if self.required.is_present() && self.default.is_present() {
62 Err(
63 Error::custom("Field cannot use `required` and `default`at the same time.")
64 .with_span(&self.default.span()),
65 )
66 } else {
67 Ok(self)
68 }
69 }
70}
71
72#[derive(Debug, FromDeriveInput)]
73#[darling(attributes(constructor), supports(struct_named))]
74struct ConstructorLite {
75 vis: Visibility,
76 ident: Ident,
77 generics: Generics,
78 data: Data<(), Field>,
79
80 visibility: Option<Visibility>,
81 name: Option<Ident>,
82}
83impl ConstructorLite {
84 fn constructor(&self) -> darling::Result<proc_macro::TokenStream> {
85 let Self {
86 vis,
87 ident,
88 generics,
89 data,
90 visibility,
91 name,
92 } = self;
93
94 let Data::Struct(fields) = data else {
95 return Err(Error::custom("ConstructorLite supports only structs."));
96 };
97
98 let mut arguments = Vec::new();
99 let mut required_field_idents = Vec::new();
100 let mut optional_field_idents = Vec::new();
101
102 for Field {
103 ident,
104 ty,
105 required,
106 default,
107 } in fields.iter()
108 {
109 if required.is_present() {
110 arguments.push(quote!(#ident: #ty));
111 required_field_idents.push(ident);
112 continue;
113 }
114 if default.is_present() {
115 optional_field_idents.push(ident);
116 continue;
117 }
118
119 if let Type::Path(ty) = &ty {
120 if path_is_option(&ty.path) {
121 optional_field_idents.push(ident);
122 } else {
123 arguments.push(quote!(#ident: #ty));
124 required_field_idents.push(ident);
125 }
126 }
127 }
128
129 let mut generics_definitions = generics.clone();
130 generics_definitions.where_clause = None;
131 for param in &mut generics_definitions.params {
132 match param {
133 GenericParam::Lifetime(..) => {}
134 GenericParam::Type(param) => {
135 param.default = None;
136 }
137 GenericParam::Const(param) => {
138 param.default = None;
139 }
140 }
141 }
142
143 let mut generics = generics.clone();
144 for param in &mut generics.params {
145 match param {
146 GenericParam::Lifetime(param) => {
147 param.bounds.clear();
148 }
149 GenericParam::Type(param) => {
150 param.bounds.clear();
151 param.default = None;
152 }
153 GenericParam::Const(param) => {
154 param.default = None;
155 }
156 }
157 }
158 let where_clause = generics.where_clause.take();
159
160 let vis = visibility.as_ref().unwrap_or(vis);
161 let name: Ident = name
162 .clone()
163 .unwrap_or_else(|| Ident::new("new", Span::call_site()));
164
165 let constructor = quote!(
166 impl #generics_definitions #ident #generics #where_clause {
167 #vis fn #name ( #( #arguments ),* ) -> Self {
168 Self {
169 #(
170 #required_field_idents,
171 )*
172 #(
173 #optional_field_idents: Default::default(),
174 )*
175 }
176 }
177 }
178 );
179
180 Ok(constructor.into())
181 }
182}
183
184fn path_is_option(path: &Path) -> bool {
185 if path.leading_colon.is_none()
187 && path.segments.len() == 1
188 && path.segments.first().unwrap().ident == "Option"
189 {
190 return true;
191 }
192
193 let mut segments = path.segments.iter();
194
195 segments
200 .next()
201 .map(|seg| seg.ident == "core" || seg.ident == "std")
202 .unwrap_or(false)
203 && segments
204 .next()
205 .map(|seg| seg.ident == "option")
206 .unwrap_or(false)
207 && segments
208 .next()
209 .map(|seg| seg.ident == "Option")
210 .unwrap_or(false)
211}
212
213#[proc_macro_derive(ConstructorLite, attributes(constructor))]
217pub fn constructor_lite_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
218 ConstructorLite::from_derive_input(&parse_macro_input!(input as syn::DeriveInput))
219 .and_then(|constructor| constructor.constructor())
220 .unwrap_or_else(|e| e.write_errors().into())
221}