1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use syn::{parse_quote, punctuated::Punctuated, Attribute, DeriveInput, Meta, NestedMeta, Token};
use crate::{meta_list_contains, nested_meta_to_ident};
pub trait DeriveInputDeriveExt {
fn append_derives(&mut self, derives: Punctuated<NestedMeta, Token![,]>);
}
impl DeriveInputDeriveExt for DeriveInput {
fn append_derives(&mut self, derives_to_append: Punctuated<NestedMeta, Token![,]>) {
let attr_derives_existing = self
.attrs
.iter_mut()
.filter(|attr| attr.path.is_ident("derive"))
.filter_map(|attr| match attr.parse_meta() {
Ok(Meta::List(meta_list)) => Some((attr, meta_list)),
_ => None,
})
.next();
if let Some((attr, mut derives_existing)) = attr_derives_existing {
let superfluous = derives_to_append
.iter()
.filter(|derive_to_append| meta_list_contains(&derives_existing, derive_to_append))
.filter_map(nested_meta_to_ident)
.map(|ident| format!("{}", ident))
.collect::<Vec<_>>();
if !superfluous.is_empty() {
panic!(
"The following are automatically derived when this attribute is used:\n\
{:?}",
superfluous
);
} else {
derives_existing.nested.extend(derives_to_append);
*attr = parse_quote!(#[#derives_existing]);
}
} else {
let derive_attribute: Attribute = parse_quote!(#[derive(#derives_to_append)]);
self.attrs.push(derive_attribute);
}
}
}
#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;
use syn::{parse_quote, DeriveInput};
use super::DeriveInputDeriveExt;
#[test]
fn append_derives_creates_attr_when_attr_does_not_exist() {
let mut ast: DeriveInput = parse_quote!(
struct Struct;
);
let derives = parse_quote!(Clone, Copy);
ast.append_derives(derives);
let ast_expected: DeriveInput = parse_quote! {
#[derive(Clone, Copy)]
struct Struct;
};
assert_eq!(ast_expected, ast);
}
#[test]
fn append_derives_appends_to_attr_when_attr_exists() {
let mut ast: DeriveInput = parse_quote!(
#[derive(Debug)]
struct Struct;
);
let derives = parse_quote!(Clone, Copy);
ast.append_derives(derives);
let ast_expected: DeriveInput = parse_quote! {
#[derive(Debug, Clone, Copy)]
struct Struct;
};
assert_eq!(ast_expected, ast);
}
#[test]
#[should_panic(
expected = "The following are automatically derived when this attribute is used:\n\
[\"Clone\", \"Copy\"]"
)]
fn append_derives_panics_when_derives_exist() {
let mut ast: DeriveInput = parse_quote!(
#[derive(Clone, Copy, Debug)]
struct Struct;
);
let derives = parse_quote!(Clone, Copy, Default);
ast.append_derives(derives);
}
}