Skip to content

Commit 971ceee

Browse files
Add impl_attrs feature.
1 parent 5da5cce commit 971ceee

File tree

3 files changed

+56
-21
lines changed

3 files changed

+56
-21
lines changed

src/generate.rs

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
22
use proc_macro_error2::abort;
33
use syn::{
4-
self, ext::IdentExt, spanned::Spanned, Expr, Field, Lit, Meta, MetaNameValue, Visibility,
4+
self, ext::IdentExt, spanned::Spanned, Attribute, Expr, Field, Lit, Meta, MetaNameValue,
5+
Visibility,
56
};
67

78
use self::GenMode::{Get, GetClone, GetCopy, GetMut, Set, SetWith};
@@ -10,6 +11,7 @@ use super::parse_attr;
1011
pub struct GenParams {
1112
pub mode: GenMode,
1213
pub global_attr: Option<Meta>,
14+
pub impl_attrs: Vec<Attribute>,
1315
}
1416

1517
#[derive(PartialEq, Eq, Copy, Clone)]
@@ -58,7 +60,7 @@ impl GenMode {
5860
}
5961

6062
// Helper function to extract string from Expr
61-
fn expr_to_string(expr: &Expr) -> Option<String> {
63+
pub(crate) fn expr_to_string(expr: &Expr) -> Option<String> {
6264
if let Expr::Lit(expr_lit) = expr {
6365
if let Lit::Str(s) = &expr_lit.lit {
6466
Some(s.value())
@@ -111,7 +113,7 @@ fn has_prefix_attr(f: &Field, params: &GenParams) -> bool {
111113
let field_attr_has_prefix = f
112114
.attrs
113115
.iter()
114-
.filter_map(|attr| parse_attr(attr, params.mode))
116+
.filter_map(|attr| parse_attr(attr, params.mode, false).0)
115117
.find(|meta| {
116118
meta.path().is_ident("get")
117119
|| meta.path().is_ident("get_clone")
@@ -161,7 +163,7 @@ pub fn implement(field: &Field, params: &GenParams) -> TokenStream2 {
161163
let attr = field
162164
.attrs
163165
.iter()
164-
.filter_map(|v| parse_attr(v, params.mode))
166+
.filter_map(|v| parse_attr(v, params.mode, false).0)
165167
.last()
166168
.or_else(|| params.global_attr.clone());
167169

@@ -236,7 +238,7 @@ pub fn implement_for_unnamed(field: &Field, params: &GenParams) -> TokenStream2
236238
let attr = field
237239
.attrs
238240
.iter()
239-
.filter_map(|v| parse_attr(v, params.mode))
241+
.filter_map(|v| parse_attr(v, params.mode, false).0)
240242
.last()
241243
.or_else(|| params.global_attr.clone());
242244
let ty = field.ty.clone();

src/lib.rs

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -209,11 +209,11 @@ use proc_macro::TokenStream;
209209
use proc_macro2::TokenStream as TokenStream2;
210210
use proc_macro_error2::{abort, abort_call_site, proc_macro_error};
211211
use syn::{
212-
parse_macro_input, punctuated::Punctuated, spanned::Spanned, Attribute, Data, DataStruct,
213-
DeriveInput, Fields, Meta, Token,
212+
parse_macro_input, parse_str, punctuated::Punctuated, spanned::Spanned, Attribute, Data,
213+
DataStruct, DeriveInput, Fields, ItemImpl, Meta, Token,
214214
};
215215

216-
use crate::generate::{GenMode, GenParams};
216+
use crate::generate::{expr_to_string, GenMode, GenParams};
217217

218218
mod generate;
219219

@@ -272,21 +272,47 @@ pub fn with_setters(input: TokenStream) -> TokenStream {
272272
}
273273

274274
fn make_params(attrs: &[Attribute], mode: GenMode) -> GenParams {
275+
let mut impl_attrs = vec![];
275276
GenParams {
276277
mode,
277-
global_attr: attrs.iter().filter_map(|v| parse_attr(v, mode)).last(),
278+
global_attr: attrs
279+
.iter()
280+
.filter_map(|v| {
281+
let (attr, impl_attrs_exist) = parse_attr(v, mode, true);
282+
if let Some(Meta::NameValue(code)) = &impl_attrs_exist {
283+
match expr_to_string(&code.value) {
284+
Some(code_str) => {
285+
match parse_str::<ItemImpl>(&format!("{} impl _ {{}}", code_str)) {
286+
Ok(parsed_impl) => impl_attrs.extend(parsed_impl.attrs),
287+
Err(_) => abort!(
288+
code.value.span(),
289+
"Syntax error, expected attributes like #[..]."
290+
),
291+
}
292+
}
293+
None => abort!(code.value.span(), "Expected string."),
294+
}
295+
}
296+
attr
297+
})
298+
.last(),
299+
impl_attrs,
278300
}
279301
}
280302

281-
fn parse_attr(attr: &Attribute, mode: GenMode) -> Option<Meta> {
303+
fn parse_attr(
304+
attr: &Attribute,
305+
mode: GenMode,
306+
globally_called: bool,
307+
) -> (Option<Meta>, Option<Meta>) {
282308
if attr.path().is_ident("getset") {
283309
let meta_list = match attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
284310
{
285311
Ok(list) => list,
286312
Err(e) => abort!(attr.span(), "Failed to parse getset attribute: {}", e),
287313
};
288314

289-
let (last, skip, mut collected) = meta_list
315+
let (last, skip, impl_attrs, mut collected) = meta_list
290316
.into_iter()
291317
.inspect(|meta| {
292318
if !(meta.path().is_ident("get")
@@ -295,21 +321,24 @@ fn parse_attr(attr: &Attribute, mode: GenMode) -> Option<Meta> {
295321
|| meta.path().is_ident("get_mut")
296322
|| meta.path().is_ident("set")
297323
|| meta.path().is_ident("set_with")
298-
|| meta.path().is_ident("skip"))
324+
|| meta.path().is_ident("skip")
325+
|| (meta.path().is_ident("impl_attrs") && globally_called))
299326
{
300327
abort!(meta.path().span(), "unknown setter or getter")
301328
}
302329
})
303330
.fold(
304-
(None, None, Vec::new()),
305-
|(last, skip, mut collected), meta| {
331+
(None, None, None, Vec::new()),
332+
|(last, skip, impl_attrs, mut collected), meta| {
306333
if meta.path().is_ident(mode.name()) {
307-
(Some(meta), skip, collected)
334+
(Some(meta), skip, impl_attrs, collected)
308335
} else if meta.path().is_ident("skip") {
309-
(last, Some(meta), collected)
336+
(last, Some(meta), impl_attrs, collected)
337+
} else if meta.path().is_ident("impl_attrs") {
338+
(last, skip, Some(meta), collected)
310339
} else {
311340
collected.push(meta);
312-
(last, skip, collected)
341+
(last, skip, impl_attrs, collected)
313342
}
314343
},
315344
);
@@ -318,26 +347,27 @@ fn parse_attr(attr: &Attribute, mode: GenMode) -> Option<Meta> {
318347
// Check if there is any setter or getter used with skip, which is
319348
// forbidden.
320349
if last.is_none() && collected.is_empty() {
321-
skip
350+
(skip, impl_attrs)
322351
} else {
323352
abort!(
324353
last.or_else(|| collected.pop()).unwrap().path().span(),
325354
"use of setters and getters with skip is invalid"
326355
);
327356
}
328357
} else {
329-
last
358+
(last, impl_attrs)
330359
}
331360
} else if attr.path().is_ident(mode.name()) {
332361
// If skip is not used, return the last occurrence of matching
333362
// setter/getter, if there is any.
334-
attr.meta.clone().into()
363+
(attr.meta.clone().into(), None)
335364
} else {
336-
None
365+
(None, None)
337366
}
338367
}
339368

340369
fn produce(ast: &DeriveInput, params: &GenParams) -> TokenStream2 {
370+
let impl_attrs = &params.impl_attrs;
341371
let name = &ast.ident;
342372
let generics = &ast.generics;
343373
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
@@ -354,6 +384,7 @@ fn produce(ast: &DeriveInput, params: &GenParams) -> TokenStream2 {
354384
let generated = generate::implement_for_unnamed(field, params);
355385

356386
quote! {
387+
#(#impl_attrs)*
357388
impl #impl_generics #name #ty_generics #where_clause {
358389
#generated
359390
}
@@ -362,6 +393,7 @@ fn produce(ast: &DeriveInput, params: &GenParams) -> TokenStream2 {
362393
let generated = fields.iter().map(|f| generate::implement(f, params));
363394

364395
quote! {
396+
#(#impl_attrs)*
365397
impl #impl_generics #name #ty_generics #where_clause {
366398
#(#generated)*
367399
}

tests/impl_attrs.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use fixture::add_1_to_implementation;
12
use getset::{CloneGetters, CopyGetters};
23

34
#[derive(CopyGetters, CloneGetters)]

0 commit comments

Comments
 (0)