codama_syn_helpers/extensions/
attribute.rs1use super::PathExtension;
2use syn::{punctuated::Punctuated, Attribute};
3
4pub trait AttributeExtension {
5 fn get_self(&self) -> &Attribute;
6
7 fn parse_comma_args<T: syn::parse::Parse>(&self) -> syn::Result<Vec<T>> {
9 self.get_self()
10 .parse_args_with(Punctuated::<T, syn::Token![,]>::parse_terminated)
11 .map(|metas| metas.into_iter().collect::<Vec<_>>())
12 }
13
14 fn unfeatured_all(&self) -> Vec<Attribute> {
19 let this = self.get_self();
20 if !this.path().is_strict("cfg_attr") {
21 return vec![];
22 }
23 let metas = match this.parse_comma_args::<syn::Meta>() {
24 Ok(m) => m,
25 Err(_) => return vec![],
26 };
27 if metas.len() < 2 {
28 return vec![];
29 }
30 match &metas[0] {
32 syn::Meta::NameValue(m) if m.path.is_strict("feature") => (),
33 _ => return vec![],
34 }
35 metas[1..]
37 .iter()
38 .map(|meta| Attribute {
39 pound_token: this.pound_token,
40 style: this.style,
41 bracket_token: this.bracket_token,
42 meta: meta.clone(),
43 })
44 .collect()
45 }
46
47 fn unfeatured(&self) -> Option<Attribute> {
52 self.unfeatured_all().into_iter().next()
53 }
54}
55
56impl AttributeExtension for Attribute {
57 fn get_self(&self) -> &Attribute {
58 self
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use quote::{quote, ToTokens};
66 use syn::parse_quote;
67
68 #[test]
69 fn parse_comma_args_ok() {
70 let attribute: Attribute = parse_quote! { #[foo(42, "bar")] };
71 let args = attribute.parse_comma_args::<syn::Lit>().unwrap();
72 assert_eq!(args.len(), 2);
73 }
74
75 #[test]
76 fn parse_comma_args_err() {
77 let attribute: Attribute = parse_quote! { #[foo] };
78 let args = attribute.parse_comma_args::<syn::Path>();
79 assert!(args.is_err());
80 }
81
82 #[test]
83 fn unfeatured_single() {
84 let attribute: Attribute =
85 parse_quote! { #[cfg_attr(feature = "some_feature", derive(Debug))] };
86 let unfeatured = attribute.unfeatured();
87 assert_eq!(
88 unfeatured.to_token_stream().to_string(),
89 quote! { #[derive(Debug)] }.to_string()
90 );
91 }
92
93 #[test]
94 fn unfeatured_multiple_returns_first() {
95 let attribute: Attribute =
96 parse_quote! { #[cfg_attr(feature = "x", derive(Debug), codama(foo))] };
97 let unfeatured = attribute.unfeatured();
98 assert_eq!(
99 unfeatured.to_token_stream().to_string(),
100 quote! { #[derive(Debug)] }.to_string()
101 );
102 }
103
104 #[test]
105 fn unfeatured_not_cfg_attr() {
106 let attribute: Attribute = parse_quote! { #[derive(Debug)] };
107 let unfeatured = attribute.unfeatured();
108 assert_eq!(unfeatured, None);
109 }
110
111 #[test]
112 fn unfeatured_all_single() {
113 let attribute: Attribute = parse_quote! { #[cfg_attr(feature = "x", derive(Debug))] };
114 let all = attribute.unfeatured_all();
115 assert_eq!(all.len(), 1);
116 assert_eq!(
117 all[0].to_token_stream().to_string(),
118 quote! { #[derive(Debug)] }.to_string()
119 );
120 }
121
122 #[test]
123 fn unfeatured_all_multiple() {
124 let attribute: Attribute = parse_quote! {
125 #[cfg_attr(feature = "codama", codama(account(name = "stake")), codama(account(name = "auth")))]
126 };
127 let all = attribute.unfeatured_all();
128 assert_eq!(all.len(), 2);
129 assert_eq!(
130 all[0].to_token_stream().to_string(),
131 quote! { #[codama(account(name = "stake"))] }.to_string()
132 );
133 assert_eq!(
134 all[1].to_token_stream().to_string(),
135 quote! { #[codama(account(name = "auth"))] }.to_string()
136 );
137 }
138
139 #[test]
140 fn unfeatured_all_not_cfg_attr() {
141 let attribute: Attribute = parse_quote! { #[derive(Debug)] };
142 let all = attribute.unfeatured_all();
143 assert!(all.is_empty());
144 }
145
146 #[test]
147 fn unfeatured_all_mixed_attrs() {
148 let attribute: Attribute = parse_quote! {
149 #[cfg_attr(feature = "x", derive(Debug), repr(u8), codama(foo))]
150 };
151 let all = attribute.unfeatured_all();
152 assert_eq!(all.len(), 3);
153 assert_eq!(
154 all[0].to_token_stream().to_string(),
155 quote! { #[derive(Debug)] }.to_string()
156 );
157 assert_eq!(
158 all[1].to_token_stream().to_string(),
159 quote! { #[repr(u8)] }.to_string()
160 );
161 assert_eq!(
162 all[2].to_token_stream().to_string(),
163 quote! { #[codama(foo)] }.to_string()
164 );
165 }
166}