1use super::Meta;
16use syn::{
17 parse::{
18 Parse,
19 ParseStream,
20 },
21 punctuated::Punctuated,
22 Token,
23};
24
25#[derive(Clone, Debug, PartialEq, Eq)]
30pub struct AttributeArgs {
31 args: Punctuated<Meta, Token![,]>,
32}
33
34impl quote::ToTokens for AttributeArgs {
35 fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
36 self.args.to_tokens(tokens)
37 }
38}
39
40impl IntoIterator for AttributeArgs {
41 type Item = Meta;
42 type IntoIter = syn::punctuated::IntoIter<Meta>;
43
44 fn into_iter(self) -> Self::IntoIter {
45 self.args.into_iter()
46 }
47}
48
49impl Parse for AttributeArgs {
50 fn parse(input: ParseStream) -> Result<Self, syn::Error> {
51 Ok(Self {
52 args: Punctuated::parse_terminated(input)?,
53 })
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use crate::ast::{
61 MetaNameValue,
62 MetaValue,
63 };
64 use quote::quote;
65
66 impl AttributeArgs {
67 pub fn new<I>(args: I) -> Self
69 where
70 I: IntoIterator<Item = Meta>,
71 {
72 Self {
73 args: args.into_iter().collect(),
74 }
75 }
76 }
77
78 #[test]
79 fn empty_works() {
80 assert_eq!(
81 syn::parse2::<AttributeArgs>(quote! {}).unwrap(),
82 AttributeArgs::new(vec![])
83 )
84 }
85
86 #[test]
87 fn flag_works() {
88 assert_eq!(
89 syn::parse2::<AttributeArgs>(quote! { flag }).unwrap(),
90 AttributeArgs::new(vec![Meta::Path(syn::parse_quote! { flag })])
91 )
92 }
93
94 #[test]
95 fn literal_bool_value_works() {
96 assert_eq!(
97 syn::parse2::<AttributeArgs>(quote! { name = true }).unwrap(),
98 AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
99 name: syn::parse_quote! { name },
100 eq_token: syn::parse_quote! { = },
101 value: MetaValue::Lit(syn::parse_quote! { true }),
102 })])
103 )
104 }
105
106 #[test]
107 fn literal_str_value_works() {
108 assert_eq!(
109 syn::parse2::<AttributeArgs>(quote! { name = "string literal" }).unwrap(),
110 AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
111 name: syn::parse_quote! { name },
112 eq_token: syn::parse_quote! { = },
113 value: MetaValue::Lit(syn::parse_quote! { "string literal" }),
114 })])
115 )
116 }
117
118 #[test]
119 fn ident_value_works() {
120 assert_eq!(
121 syn::parse2::<AttributeArgs>(quote! { name = MyIdentifier }).unwrap(),
122 AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
123 name: syn::parse_quote! { name },
124 eq_token: syn::parse_quote! { = },
125 value: MetaValue::Path(syn::parse_quote! { MyIdentifier }),
126 })])
127 )
128 }
129
130 #[test]
131 fn root_path_value_works() {
132 assert_eq!(
133 syn::parse2::<AttributeArgs>(quote! { name = ::this::is::my::Path }).unwrap(),
134 AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
135 name: syn::parse_quote! { name },
136 eq_token: syn::parse_quote! { = },
137 value: MetaValue::Path(syn::parse_quote! { ::this::is::my::Path }),
138 })])
139 )
140 }
141
142 #[test]
143 fn relative_path_value_works() {
144 assert_eq!(
145 syn::parse2::<AttributeArgs>(quote! { name = this::is::my::relative::Path })
146 .unwrap(),
147 AttributeArgs::new(vec![Meta::NameValue(MetaNameValue {
148 name: syn::parse_quote! { name },
149 eq_token: syn::parse_quote! { = },
150 value: MetaValue::Path(
151 syn::parse_quote! { this::is::my::relative::Path }
152 ),
153 })])
154 )
155 }
156
157 #[test]
158 fn trailing_comma_works() {
159 let mut expected_args = Punctuated::new();
160 expected_args.push_value(Meta::NameValue(MetaNameValue {
161 name: syn::parse_quote! { name },
162 eq_token: syn::parse_quote! { = },
163 value: MetaValue::Path(syn::parse_quote! { value }),
164 }));
165 expected_args.push_punct(<Token![,]>::default());
166 assert_eq!(
167 syn::parse2::<AttributeArgs>(quote! { name = value, }).unwrap(),
168 AttributeArgs {
169 args: expected_args,
170 }
171 )
172 }
173
174 #[test]
175 fn many_mixed_works() {
176 assert_eq!(
177 syn::parse2::<AttributeArgs>(quote! {
178 flag,
179 name1 = ::root::Path,
180 name2 = false,
181 name3 = "string literal",
182 name4 = 42,
183 name5 = 7.7
184 })
185 .unwrap(),
186 AttributeArgs::new(vec![
187 Meta::Path(syn::parse_quote! { flag }),
188 Meta::NameValue(MetaNameValue {
189 name: syn::parse_quote! { name1 },
190 eq_token: syn::parse_quote! { = },
191 value: MetaValue::Path(syn::parse_quote! { ::root::Path }),
192 }),
193 Meta::NameValue(MetaNameValue {
194 name: syn::parse_quote! { name2 },
195 eq_token: syn::parse_quote! { = },
196 value: MetaValue::Lit(syn::parse_quote! { false }),
197 }),
198 Meta::NameValue(MetaNameValue {
199 name: syn::parse_quote! { name3 },
200 eq_token: syn::parse_quote! { = },
201 value: MetaValue::Lit(syn::parse_quote! { "string literal" }),
202 }),
203 Meta::NameValue(MetaNameValue {
204 name: syn::parse_quote! { name4 },
205 eq_token: syn::parse_quote! { = },
206 value: MetaValue::Lit(syn::parse_quote! { 42 }),
207 }),
208 Meta::NameValue(MetaNameValue {
209 name: syn::parse_quote! { name5 },
210 eq_token: syn::parse_quote! { = },
211 value: MetaValue::Lit(syn::parse_quote! { 7.7 }),
212 }),
213 ])
214 )
215 }
216}