serde_const_default/
lib.rs1use proc_macro::TokenStream;
34use quote::{format_ident, quote};
35use syn::punctuated::Punctuated;
36use syn::{
37 Attribute, Error, Expr, Field, Fields, ItemStruct, LitStr, Meta, Token, parse_macro_input,
38 parse_quote,
39};
40
41#[proc_macro_attribute]
91pub fn serde_const_default(attr: TokenStream, item: TokenStream) -> TokenStream {
92 if !attr.is_empty() {
93 return Error::new_spanned(
94 proc_macro2::TokenStream::from(attr),
95 "`serde_const_default` does not accept arguments",
96 )
97 .to_compile_error()
98 .into();
99 }
100
101 let item = parse_macro_input!(item as ItemStruct);
102 expand_serde_const_default(item)
103 .unwrap_or_else(Error::into_compile_error)
104 .into()
105}
106
107fn expand_serde_const_default(mut item: ItemStruct) -> Result<proc_macro2::TokenStream, Error> {
108 let mut helpers = Vec::new();
109 let struct_ident = item.ident.clone();
110
111 match &mut item.fields {
112 Fields::Named(fields) => {
113 for (index, field) in fields.named.iter_mut().enumerate() {
114 if let Some(helper) = rewrite_field(&struct_ident, index, field)? {
115 helpers.push(helper);
116 }
117 }
118 }
119 Fields::Unnamed(fields) => {
120 for field in &fields.unnamed {
121 if has_const_default_attr(field) {
122 return Err(Error::new_spanned(
126 field,
127 "`serde_const_default` only supports named fields",
128 ));
129 }
130 }
131 }
132 Fields::Unit => {}
133 }
134
135 Ok(quote! {
136 #(#helpers)*
137 #item
138 })
139}
140
141fn rewrite_field(
142 struct_ident: &syn::Ident,
143 index: usize,
144 field: &mut Field,
145) -> Result<Option<proc_macro2::TokenStream>, Error> {
146 let Some(default) = take_const_default_attr(&mut field.attrs)? else {
147 return Ok(None);
148 };
149
150 if let Some(attr) = field.attrs.iter().find(|attr| serde_attr_has_default(attr)) {
151 return Err(Error::new_spanned(
154 attr,
155 "`const_default` cannot be combined with `#[serde(default)]`",
156 ));
157 }
158
159 let Some(field_ident) = field.ident.as_ref() else {
160 return Err(Error::new_spanned(
161 &*field,
162 "`serde_const_default` only supports named fields",
163 ));
164 };
165 let helper_ident = format_ident!(
166 "__serde_const_default_{}_{}_{}",
167 struct_ident,
168 field_ident,
169 index
170 );
171 let helper_name = LitStr::new(&helper_ident.to_string(), helper_ident.span());
172 let ty = &field.ty;
173 let expr = default.expr;
174 let (function_qualifier, body) = match default.kind {
175 DefaultKind::Plain => (quote! { const }, quote! { #expr }),
176 DefaultKind::From => (quote! {}, quote! { ::core::convert::From::from(#expr) }),
177 };
178
179 field.attrs.push(parse_quote! {
183 #[serde(default = #helper_name)]
184 });
185
186 Ok(Some(quote! {
187 #[allow(non_snake_case)]
188 #function_qualifier fn #helper_ident() -> #ty {
189 #body
190 }
191 }))
192}
193
194fn take_const_default_attr(attrs: &mut Vec<Attribute>) -> Result<Option<ConstDefault>, Error> {
195 let mut default = None;
196 let mut error = None::<Error>;
197 let mut retained = Vec::with_capacity(attrs.len());
198
199 for attr in attrs.drain(..) {
200 match parse_const_default_attr(&attr)? {
204 Some(parsed) => {
205 if default.is_some() {
206 let duplicate_error = Error::new_spanned(
207 attr,
208 "only one const default attribute is allowed per field",
209 );
210
211 if let Some(error) = &mut error {
212 error.combine(duplicate_error);
213 } else {
214 error = Some(duplicate_error);
215 }
216 } else {
217 default = Some(parsed);
218 }
219 }
220 None => retained.push(attr),
221 }
222 }
223
224 *attrs = retained;
225
226 if let Some(error) = error {
227 Err(error)
228 } else {
229 Ok(default)
230 }
231}
232
233fn parse_const_default_attr(attr: &Attribute) -> Result<Option<ConstDefault>, Error> {
234 if attr.path().is_ident("const_default") {
235 let Meta::NameValue(name_value) = &attr.meta else {
236 return Err(Error::new_spanned(
237 attr,
238 "expected `#[const_default = EXPR]`",
239 ));
240 };
241
242 return Ok(Some(ConstDefault {
243 kind: DefaultKind::Plain,
244 expr: name_value.value.clone(),
245 }));
246 }
247
248 if attr.path().is_ident("const_default_from") {
249 return Ok(Some(ConstDefault {
250 kind: DefaultKind::From,
251 expr: attr.parse_args::<Expr>()?,
252 }));
253 }
254
255 Ok(None)
256}
257
258fn has_const_default_attr(field: &Field) -> bool {
260 field.attrs.iter().any(|attr| {
261 attr.path().is_ident("const_default") || attr.path().is_ident("const_default_from")
262 })
263}
264
265fn serde_attr_has_default(attr: &Attribute) -> bool {
267 if !attr.path().is_ident("serde") {
268 return false;
269 }
270
271 let Meta::List(list) = &attr.meta else {
272 return false;
273 };
274
275 list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
276 .map(|metas| {
277 metas.iter().any(|meta| match meta {
278 Meta::Path(path) => path.is_ident("default"),
279 Meta::NameValue(name_value) => name_value.path.is_ident("default"),
280 Meta::List(list) => list.path.is_ident("default"),
281 })
282 })
283 .unwrap_or(false)
284}
285
286struct ConstDefault {
288 kind: DefaultKind,
289 expr: Expr,
290}
291
292enum DefaultKind {
295 Plain,
296 From,
297}