darling_core/ast/data/
nested_meta.rs1use proc_macro2::{Delimiter, TokenStream, TokenTree};
2use quote::ToTokens;
3use syn::{
4 ext::IdentExt,
5 parse::{discouraged::Speculative, ParseStream, Parser},
6 punctuated::Punctuated,
7 token::{self, Brace, Bracket, Paren},
8 Expr, ExprLit, Ident, Lit, MacroDelimiter, Meta, MetaList, MetaNameValue, Path, PathSegment,
9 Token,
10};
11
12fn parse_meta_path<'a>(input: ParseStream<'a>) -> syn::Result<Path> {
13 Ok(Path {
14 leading_colon: input.parse()?,
15 segments: {
16 let mut segments = Punctuated::new();
17 loop {
18 if !input.peek(Ident::peek_any) {
20 break;
21 }
22
23 let ident = Ident::parse_any(input)?;
24 segments.push_value(PathSegment::from(ident));
25 if !input.peek(Token![::]) {
26 break;
27 }
28 let punct = input.parse()?;
29 segments.push_punct(punct);
30 }
31 if segments.is_empty() {
32 return Err(input.parse::<Ident>().unwrap_err());
33 } else if segments.trailing_punct() {
34 return Err(input.error("expected path segment after `::`"));
35 }
36 segments
37 },
38 })
39}
40
41fn parse_meta_after_path<'a>(path: Path, input: ParseStream<'a>) -> syn::Result<Meta> {
42 if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) {
43 parse_meta_list_after_path(path, input).map(Meta::List)
44 } else if input.peek(Token![=]) {
45 parse_meta_name_value_after_path(path, input).map(Meta::NameValue)
46 } else {
47 Ok(Meta::Path(path))
48 }
49}
50
51fn parse_meta_list_after_path<'a>(path: Path, input: ParseStream<'a>) -> syn::Result<MetaList> {
52 let (delimiter, tokens) = input.step(|cursor| {
53 if let Some((TokenTree::Group(g), rest)) = cursor.token_tree() {
54 let span = g.delim_span();
55 let delimiter = match g.delimiter() {
56 Delimiter::Parenthesis => MacroDelimiter::Paren(Paren(span)),
57 Delimiter::Brace => MacroDelimiter::Brace(Brace(span)),
58 Delimiter::Bracket => MacroDelimiter::Bracket(Bracket(span)),
59 Delimiter::None => {
60 return Err(cursor.error("expected delimiter"));
61 }
62 };
63 Ok(((delimiter, g.stream()), rest))
64 } else {
65 Err(cursor.error("expected delimiter"))
66 }
67 })?;
68 Ok(MetaList {
69 path,
70 delimiter,
71 tokens,
72 })
73}
74
75fn parse_meta_name_value_after_path<'a>(
76 path: Path,
77 input: ParseStream<'a>,
78) -> syn::Result<MetaNameValue> {
79 let eq_token: Token![=] = input.parse()?;
80 let ahead = input.fork();
81 let lit: Option<Lit> = ahead.parse()?;
82 let value = if let (Some(lit), true) = (lit, ahead.is_empty()) {
83 input.advance_to(&ahead);
84 Expr::Lit(ExprLit {
85 attrs: Vec::new(),
86 lit,
87 })
88 } else if input.peek(Token![#]) && input.peek2(token::Bracket) {
89 return Err(input.error("unexpected attribute inside of attribute"));
90 } else {
91 input.parse()?
92 };
93 Ok(MetaNameValue {
94 path,
95 eq_token,
96 value,
97 })
98}
99
100#[derive(Debug, Clone)]
101#[allow(clippy::large_enum_variant)]
103pub enum NestedMeta {
104 Meta(syn::Meta),
105 Lit(syn::Lit),
106}
107
108impl NestedMeta {
109 pub fn parse_meta_list(tokens: TokenStream) -> syn::Result<Vec<Self>> {
110 syn::punctuated::Punctuated::<NestedMeta, Token![,]>::parse_terminated
111 .parse2(tokens)
112 .map(|punctuated| punctuated.into_iter().collect())
113 }
114}
115
116impl syn::parse::Parse for NestedMeta {
117 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
118 if input.peek(syn::Lit) && !(input.peek(syn::LitBool) && input.peek2(syn::Token![=])) {
135 input.parse().map(Self::Lit)
136 } else if input.peek(syn::Ident::peek_any) {
137 let path = parse_meta_path(input)?;
138 parse_meta_after_path(path, input).map(Self::Meta)
139 } else {
140 Err(input.error("expected identifier or literal"))
141 }
142 }
143}
144
145impl ToTokens for NestedMeta {
146 fn to_tokens(&self, tokens: &mut TokenStream) {
147 match self {
148 NestedMeta::Meta(meta) => meta.to_tokens(tokens),
149 NestedMeta::Lit(lit) => lit.to_tokens(tokens),
150 }
151 }
152}