1 use std::collections::BTreeSet;
2
3 use darling::{util, Error, FromDeriveInput, Result};
4 use syn::{parse_quote, Attribute};
5
unique_idents(attrs: Vec<Attribute>) -> Result<BTreeSet<String>>6 fn unique_idents(attrs: Vec<Attribute>) -> Result<BTreeSet<String>> {
7 let mut errors = Error::accumulator();
8 let idents = attrs
9 .into_iter()
10 .filter_map(|attr| {
11 let path = attr.path();
12 errors.handle(
13 path.get_ident()
14 .map(std::string::ToString::to_string)
15 .ok_or_else(|| {
16 Error::custom(format!("`{}` is not an ident", util::path_to_string(path)))
17 .with_span(path)
18 }),
19 )
20 })
21 .collect();
22
23 errors.finish_with(idents)
24 }
25
26 #[derive(FromDeriveInput)]
27 #[darling(attributes(a), forward_attrs)]
28 struct Receiver {
29 #[darling(with = unique_idents)]
30 attrs: BTreeSet<String>,
31 other: Option<bool>,
32 }
33
34 #[test]
succeeds_on_no_attrs()35 fn succeeds_on_no_attrs() {
36 let di = Receiver::from_derive_input(&parse_quote! {
37 struct Demo;
38 })
39 .unwrap();
40
41 assert!(di.attrs.is_empty());
42 }
43
44 #[test]
succeeds_on_valid_input()45 fn succeeds_on_valid_input() {
46 let di = Receiver::from_derive_input(&parse_quote! {
47 #[allow(dead_code)]
48 /// testing
49 #[another]
50 struct Demo;
51 })
52 .unwrap();
53
54 assert_eq!(di.attrs.len(), 3);
55 assert!(di.attrs.contains("allow"));
56 assert!(di.attrs.contains("another"));
57 assert!(di.attrs.contains("doc"));
58 assert_eq!(di.other, None);
59 }
60
61 #[test]
errors_combined_with_others()62 fn errors_combined_with_others() {
63 let e = Receiver::from_derive_input(&parse_quote! {
64 #[path::to::attr(dead_code)]
65 #[a(other = 5)]
66 struct Demo;
67 })
68 .map(|_| "Should have failed")
69 .unwrap_err();
70
71 let error = e.to_string();
72
73 assert_eq!(e.len(), 2);
74
75 // Look for the error on the field `other`
76 assert!(error.contains("at other"));
77
78 // Look for the invalid path from attrs conversion
79 assert!(error.contains("`path::to::attr`"));
80 }
81