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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
use crate::MUTATIS_ATTRIBUTE_NAME;
use syn::{punctuated::Punctuated, *};
pub struct ContainerAttributes {
/// An override of the derived mutator's name.
///
/// ```ignore
/// #[mutatis(mutator_name = FancyMutatorName)]
/// ```
pub mutator_name: Option<Ident>,
/// An optional documentation string for the derived mutator.
///
/// ```ignore
/// #[mutatis(mutator_doc = "A fancy doc comment for my derived mutator!")]
/// #[mutatis(mutator_doc = "and it can have multiple lines!")]
/// ```
pub mutator_doc: Option<Vec<LitStr>>,
/// An optional flag to specify whether the derived mutator should implement
/// `DefaultMutate` for the type or not. The default behavior is `true`.
///
/// ```ignore
/// #[mutatis(default_mutate = false)]
/// ```
pub default_mutate: Option<bool>,
/// An optional flag to specify whether the derived mutator should implement
/// `Generate` for the type or not. The default behavior is `true`.
///
/// ```ignore
/// #[mutatis(generate = false)]
/// ```
pub generate: Option<bool>,
}
impl ContainerAttributes {
pub fn from_derive_input(derive_input: &DeriveInput) -> Result<Self> {
let mut mutator_name = None;
let mut mutator_doc = None;
let mut default_mutate = None;
let mut generate = None;
for attr in &derive_input.attrs {
if !attr.path().is_ident(MUTATIS_ATTRIBUTE_NAME) {
continue;
}
let meta_list = match attr.meta {
Meta::List(ref l) => l,
_ => {
return Err(Error::new_spanned(
attr,
format!(
"invalid `{}` attribute. expected list",
MUTATIS_ATTRIBUTE_NAME
),
))
}
};
for nested_meta in
meta_list.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?
{
match nested_meta {
Meta::NameValue(MetaNameValue {
path,
value:
Expr::Path(ExprPath {
attrs,
qself: None,
path: name,
}),
..
}) if path.is_ident("mutator_name")
&& attrs.is_empty()
&& name.get_ident().is_some() =>
{
if mutator_name.is_some() {
return Err(Error::new_spanned(
attr,
format!(
"invalid `{MUTATIS_ATTRIBUTE_NAME}` attribute: duplicate `mutator_name`",
),
));
}
mutator_name = Some(name.get_ident().unwrap().clone());
}
Meta::NameValue(MetaNameValue {
path,
value:
Expr::Lit(ExprLit {
lit: Lit::Bool(bool_lit),
..
}),
..
}) if path.is_ident("default_mutate") => {
if default_mutate.is_some() {
return Err(Error::new_spanned(
attr,
format!(
"invalid `{MUTATIS_ATTRIBUTE_NAME}` attribute: duplicate `default_mutate`",
),
));
}
default_mutate = Some(bool_lit.value);
}
Meta::NameValue(MetaNameValue {
path,
value:
Expr::Lit(ExprLit {
lit: Lit::Bool(bool_lit),
..
}),
..
}) if path.is_ident("generate") => {
if generate.is_some() {
return Err(Error::new_spanned(
attr,
format!(
"invalid `{MUTATIS_ATTRIBUTE_NAME}` attribute: duplicate `generate`",
),
));
}
generate = Some(bool_lit.value);
}
Meta::NameValue(MetaNameValue {
path,
value:
Expr::Lit(ExprLit {
lit: Lit::Str(lit_str),
..
}),
..
}) if path.is_ident("mutator_doc") => {
mutator_doc.get_or_insert_with(Vec::new).push(lit_str);
}
_ => {
return Err(Error::new_spanned(
attr,
format!("invalid `{MUTATIS_ATTRIBUTE_NAME}` attribute"),
))
}
}
}
}
Ok(Self {
mutator_name,
mutator_doc,
default_mutate,
generate,
})
}
}