1use crate::{CustomExpr, ExprUnnamedStruct};
2use proc_macro2::{Ident, TokenStream};
3use quote::ToTokens;
4use syn::{
5 ext::IdentExt,
6 parenthesized,
7 parse::{Parse, ParseStream},
8 punctuated::Punctuated,
9 token, Lit, LitBool, Path, PathSegment, Token,
10};
11
12pub enum Meta {
14 Path(MetaPath),
15 List(MetaList),
16 UnnamedList(UnnamedMetaList),
17 NameValue(MetaNameValue),
18}
19
20impl Parse for Meta {
21 fn parse(input: ParseStream) -> syn::Result<Self> {
22 if input.peek(token::Paren) {
23 let lst = input.parse::<UnnamedMetaList>()?;
24 Ok(Meta::UnnamedList(lst))
25 } else {
26 let path = input.parse::<MetaPath>()?;
27 parse_meta_after_path(path, input)
28 }
29 }
30}
31
32impl ToTokens for Meta {
33 fn to_tokens(&self, tokens: &mut TokenStream) {
34 match self {
35 Meta::Path(value) => value.to_tokens(tokens),
36 Meta::List(value) => value.to_tokens(tokens),
37 Meta::UnnamedList(value) => value.to_tokens(tokens),
38 Meta::NameValue(value) => value.to_tokens(tokens),
39 }
40 }
41}
42
43pub struct MetaPath(Path);
44
45impl MetaPath {
46 pub fn get_inner(&self) -> &Path {
47 &self.0
48 }
49}
50
51impl Parse for MetaPath {
52 fn parse(input: ParseStream) -> syn::Result<Self> {
53 Ok(MetaPath(Path {
54 leading_colon: input.parse()?,
55 segments: {
56 let mut segments = Punctuated::new();
57 while input.peek(Ident::peek_any) {
58 let ident = Ident::parse_any(input)?;
59 segments.push_value(PathSegment::from(ident));
60 if !input.peek(Token![::]) {
61 break;
62 }
63 let punct = input.parse()?;
64 segments.push_punct(punct);
65 }
66 if segments.is_empty() {
67 return Err(input.error("expected path"));
68 } else if segments.trailing_punct() {
69 return Err(input.error("expected path segment"));
70 }
71 segments
72 },
73 }))
74 }
75}
76
77impl ToTokens for MetaPath {
78 fn to_tokens(&self, tokens: &mut TokenStream) {
79 self.0.to_tokens(tokens);
80 }
81}
82
83pub struct UnnamedMetaList {
84 pub paren_token: token::Paren,
85 pub nested: Punctuated<NestedMeta, Token![,]>,
86}
87
88impl Parse for UnnamedMetaList {
89 fn parse(input: ParseStream) -> syn::Result<Self> {
90 let content;
91 let paren_token = parenthesized!(content in input);
92
93 Ok(UnnamedMetaList {
94 paren_token,
95 nested: content.parse_terminated(NestedMeta::parse)?,
96 })
97 }
98}
99
100impl ToTokens for UnnamedMetaList {
101 fn to_tokens(&self, tokens: &mut TokenStream) {
102 self.paren_token.surround(tokens, |tokens| {
103 self.nested.to_tokens(tokens);
104 });
105 }
106}
107
108pub struct MetaList {
109 pub path: MetaPath,
110 pub paren_token: token::Paren,
111 pub nested: Punctuated<NestedMeta, Token![,]>,
112}
113
114impl Parse for MetaList {
115 fn parse(input: ParseStream) -> syn::Result<Self> {
116 let path = input.parse::<MetaPath>()?;
117 parse_meta_list_after_path(path, input)
118 }
119}
120
121impl ToTokens for MetaList {
122 fn to_tokens(&self, tokens: &mut TokenStream) {
123 self.path.to_tokens(tokens);
124 self.paren_token.surround(tokens, |tokens| {
125 self.nested.to_tokens(tokens);
126 });
127 }
128}
129
130pub enum MetaValue {
132 Lit(Lit),
133 ExprUnnamedStruct(ExprUnnamedStruct<CustomExpr>),
134 UnnamedMetaList(UnnamedMetaList),
135}
136
137impl Parse for MetaValue {
138 fn parse(input: ParseStream) -> syn::Result<Self> {
139 if input.peek(token::Paren) {
140 let lst = input.parse::<UnnamedMetaList>()?;
141 Ok(MetaValue::UnnamedMetaList(lst))
142 } else if input.peek(token::Brace) {
143 let obj = <ExprUnnamedStruct<CustomExpr>>::parse(input)?;
144 Ok(MetaValue::ExprUnnamedStruct(obj))
145 } else {
146 let expr = Lit::parse(input)?;
147 Ok(MetaValue::Lit(expr))
148 }
149 }
150}
151
152impl ToTokens for MetaValue {
153 fn to_tokens(&self, tokens: &mut TokenStream) {
154 match self {
155 MetaValue::Lit(value) => value.to_tokens(tokens),
156 MetaValue::ExprUnnamedStruct(value) => value.to_tokens(tokens),
157 MetaValue::UnnamedMetaList(value) => value.to_tokens(tokens),
158 }
159 }
160}
161
162pub struct MetaNameValue {
163 pub path: MetaPath,
164 pub eq_token: Token![=],
165 pub value: MetaValue,
166}
167
168impl Parse for MetaNameValue {
169 fn parse(input: ParseStream) -> syn::Result<Self> {
170 let path = input.parse::<MetaPath>()?;
171 parse_meta_name_value_after_path(path, input)
172 }
173}
174impl ToTokens for MetaNameValue {
175 fn to_tokens(&self, tokens: &mut TokenStream) {
176 self.path.to_tokens(tokens);
177 self.eq_token.to_tokens(tokens);
178 self.value.to_tokens(tokens);
179 }
180}
181
182pub enum NestedMeta {
183 Meta(Meta),
184 Lit(Lit),
185 Expr(CustomExpr),
186}
187
188impl Parse for NestedMeta {
189 fn parse(input: ParseStream) -> syn::Result<Self> {
190 if input.peek(Lit) && !(input.peek(LitBool) && input.peek2(Token![=])) {
191 input.parse().map(NestedMeta::Lit)
192 } else if input.peek(Ident::peek_any)
193 || input.peek(Token![::]) && input.peek3(Ident::peek_any)
194 {
195 input.parse().map(NestedMeta::Meta)
196 } else {
197 Err(input.error("expected identifier or literal"))
198 }
199 }
200}
201
202impl ToTokens for NestedMeta {
203 fn to_tokens(&self, tokens: &mut TokenStream) {
204 match self {
205 NestedMeta::Meta(value) => value.to_tokens(tokens),
206 NestedMeta::Expr(value) => value.to_tokens(tokens),
207 NestedMeta::Lit(value) => value.to_tokens(tokens),
208 }
209 }
210}
211
212pub fn parse_meta_after_path(path: MetaPath, input: ParseStream) -> syn::Result<Meta> {
213 if input.peek(token::Paren) {
214 parse_meta_list_after_path(path, input).map(Meta::List)
215 } else if input.peek(Token![=]) {
216 parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
217 } else {
218 Ok(Meta::Path(path))
219 }
220}
221
222fn parse_meta_list_after_path(path: MetaPath, input: ParseStream) -> syn::Result<MetaList> {
223 let content;
224 let paren_token = parenthesized!(content in input);
225 Ok(MetaList {
226 path,
227 paren_token,
228 nested: content.parse_terminated(NestedMeta::parse)?,
229 })
230}
231
232fn parse_meta_name_value_after_path(
233 path: MetaPath,
234 input: ParseStream,
235) -> syn::Result<MetaNameValue> {
236 Ok(MetaNameValue {
237 path,
238 eq_token: input.parse()?,
239 value: input.parse()?,
240 })
241}
242
243#[cfg(test)]
244mod tests {
245 use syn::{parse::Parser, Attribute};
246
247 use super::*;
248
249 #[test]
250 fn test_meta() {
251 let attrs = Parser::parse_str(Attribute::parse_outer, "#[blah(name=\"MyVal\", active)]")
252 .expect("attribute");
253 let elem = attrs
254 .first()
255 .unwrap()
256 .parse_args_with(<Punctuated<Meta, Token![,]>>::parse_terminated)
257 .expect("Could not parse the args");
258 let elem_str = elem.to_token_stream().to_string();
259
260 assert_eq!(elem_str, "name = \"MyVal\" , active");
261 }
262
263 #[test]
264 fn test_meta_list() {
265 let attrs = Parser::parse_str(Attribute::parse_outer, "#[blah(a, b, more(one=1, two=2))]")
266 .expect("attribute");
267 let elem = attrs
268 .first()
269 .unwrap()
270 .parse_args_with(<Punctuated<Meta, Token![,]>>::parse_terminated)
271 .expect("Could not parse the args");
272 let elem_str = elem.to_token_stream().to_string();
273
274 assert_eq!(elem_str, "a , b , more (one = 1 , two = 2)");
275 }
276
277 #[test]
278 fn test_meta_unnamed_struct() {
279 let attrs = Parser::parse_str(
280 Attribute::parse_outer,
281 "#[blah(name=\"MyVal\", age=33, other={name: \"ok\"})]",
282 )
283 .expect("attribute");
284 let elem = attrs
285 .first()
286 .unwrap()
287 .parse_args_with(<Punctuated<Meta, Token![,]>>::parse_terminated)
288 .expect("Could not parse the args");
289 let elem_str = elem.to_token_stream().to_string();
290
291 assert_eq!(
292 elem_str,
293 "name = \"MyVal\" , age = 33 , other = { name : \"ok\" }"
294 );
295 }
296
297 #[test]
298 fn test_meta_unnamed_list() {
299 let attrs = Parser::parse_str(Attribute::parse_outer, "#[blah(a, b, (one=1, two=2))]")
300 .expect("attribute");
301 let elem = attrs
302 .first()
303 .unwrap()
304 .parse_args_with(<Punctuated<Meta, Token![,]>>::parse_terminated)
305 .expect("Could not parse the args");
306 let elem_str = elem.to_token_stream().to_string();
307
308 assert_eq!(elem_str, "a , b , (one = 1 , two = 2)");
309 }
310
311 #[test]
312 fn test_meta_unnamed_nested_list() {
313 let attrs = Parser::parse_str(Attribute::parse_outer, "#[blah(a, b, c = (one=1, two=2))]")
314 .expect("attribute");
315 let elem = attrs
316 .first()
317 .unwrap()
318 .parse_args_with(<Punctuated<Meta, Token![,]>>::parse_terminated)
319 .expect("Could not parse the args");
320 let elem_str = elem.to_token_stream().to_string();
321
322 assert_eq!(elem_str, "a , b , c = (one = 1 , two = 2)");
323 }
324}