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
use syn::visit::{self, Visit};
use syn::{Attribute, Field, Ident, Meta, NestedMeta};
const SLOG_ATTRIBUTE: &str = "slog";
pub fn contains_named_attr(attrs: &[Attribute], name: &str) -> bool {
slog_attributes(attrs)
.into_iter()
.any(|meta| meta.path().is_ident(name))
}
pub fn slog_attributes(attrs: &[Attribute]) -> Vec<Meta> {
attrs
.iter()
.filter_map(|attr| attr.parse_meta().ok())
.filter_map(|meta| match meta {
Meta::List(list) => Some(list),
_ => None,
})
.filter(|meta_list| meta_list.path.is_ident(SLOG_ATTRIBUTE))
.flat_map(|ml| ml.nested)
.filter_map(|nested| match nested {
NestedMeta::Meta(m) => Some(m),
_ => None,
})
.collect()
}
#[derive(Debug, Default)]
pub struct CollectFields {
pub fields: Vec<Ident>,
}
impl<'a> Visit<'a> for CollectFields {
fn visit_field(&mut self, field: &Field) {
let name = field
.ident
.as_ref()
.expect("You can't use this derive on a tuple struct");
if !contains_named_attr(&field.attrs, "skip") {
self.fields.push(name.clone());
}
visit::visit_field(self, field);
}
}
#[cfg(test)]
mod tests {
use super::*;
use syn::DeriveInput;
#[test]
fn find_the_skip_attribute() {
let src = "#[slog(skip)] struct Foo {}";
let foo: DeriveInput = syn::parse_str(src).unwrap();
let attrs = &foo.attrs;
assert!(contains_named_attr(attrs, "skip"));
}
}