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, StepCursor},
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<NestedMeta> {
42 if input.peek(token::Paren) || input.peek(token::Bracket) || input.peek(token::Brace) {
43 parse_meta_list_after_path(path, input)
44 .map(Meta::List)
45 .map(NestedMeta::Meta)
46 } else if input.peek(Token![=]) {
47 Ok(match parse_meta_name_value_after_path(path, input)? {
48 MetaNameValueAnyRhs::ValidExpr(meta) => NestedMeta::Meta(Meta::NameValue(meta)),
49 MetaNameValueAnyRhs::InvalidExpr(meta) => NestedMeta::NameValueInvalidExpr(meta),
50 })
51 } else {
52 Ok(NestedMeta::Meta(Meta::Path(path)))
53 }
54}
55
56fn parse_meta_list_after_path<'a>(path: Path, input: ParseStream<'a>) -> syn::Result<MetaList> {
57 let (delimiter, tokens) = input.step(|cursor| {
58 if let Some((TokenTree::Group(g), rest)) = cursor.token_tree() {
59 let span = g.delim_span();
60 let delimiter = match g.delimiter() {
61 Delimiter::Parenthesis => MacroDelimiter::Paren(Paren(span)),
62 Delimiter::Brace => MacroDelimiter::Brace(Brace(span)),
63 Delimiter::Bracket => MacroDelimiter::Bracket(Bracket(span)),
64 Delimiter::None => {
65 return Err(cursor.error("expected delimiter"));
66 }
67 };
68 Ok(((delimiter, g.stream()), rest))
69 } else {
70 Err(cursor.error("expected delimiter"))
71 }
72 })?;
73 Ok(MetaList {
74 path,
75 delimiter,
76 tokens,
77 })
78}
79
80enum MetaNameValueAnyRhs {
81 ValidExpr(MetaNameValue),
83 InvalidExpr(MetaNameValueInvalidExpr),
85}
86
87fn parse_meta_name_value_after_path<'a>(
88 path: Path,
89 input: ParseStream<'a>,
90) -> syn::Result<MetaNameValueAnyRhs> {
91 let eq_token: Token![=] = input.parse()?;
92 let ahead = input.fork();
93 let lit: Option<Lit> = ahead.parse()?;
94 let value = if let (Some(lit), true) = (lit, ahead.is_empty()) {
95 input.advance_to(&ahead);
96 Expr::Lit(ExprLit {
97 attrs: Vec::new(),
98 lit,
99 })
100 } else if input.peek(Token![#]) && input.peek2(token::Bracket) {
101 return Err(input.error("unexpected attribute inside of attribute"));
102 } else {
103 let input_fork = input.fork();
106
107 match input.parse() {
108 Ok(expr) => expr,
109 Err(error) => {
114 fn eat_until_comma<'c>(
115 cursor: StepCursor<'c, '_>,
116 ) -> syn::Result<(TokenStream, syn::buffer::Cursor<'c>)> {
117 let mut rest = *cursor;
118 let mut ts = TokenStream::new();
119 while let Some((tt, next)) = rest.token_tree() {
120 match tt {
121 TokenTree::Punct(punct) if punct.as_char() == ',' => {
122 break;
123 }
124 tt => {
125 ts.extend([tt]);
126 rest = next
127 }
128 }
129 }
130 Ok((ts, rest))
131 }
132
133 let verbatim_input = input_fork.step(eat_until_comma)?;
137
138 input.step(eat_until_comma)?;
141
142 return Ok(MetaNameValueAnyRhs::InvalidExpr(MetaNameValueInvalidExpr {
143 path,
144 eq_token,
145 value: verbatim_input,
146 error: error.into(),
147 }));
148 }
149 }
150 };
151 Ok(MetaNameValueAnyRhs::ValidExpr(MetaNameValue {
152 path,
153 eq_token,
154 value,
155 }))
156}
157
158#[derive(Debug, Clone, PartialEq, Eq)]
159#[allow(clippy::large_enum_variant)]
161pub enum NestedMeta {
162 Meta(syn::Meta),
163 Lit(syn::Lit),
164 NameValueInvalidExpr(MetaNameValueInvalidExpr),
165}
166
167#[derive(Debug, Clone)]
187pub struct MetaNameValueInvalidExpr {
188 pub path: Path,
190 pub eq_token: Token![=],
192 pub value: TokenStream,
196 pub error: crate::Error,
204}
205
206impl ToTokens for MetaNameValueInvalidExpr {
207 fn to_tokens(&self, tokens: &mut TokenStream) {
208 self.path.to_tokens(tokens);
209 self.eq_token.to_tokens(tokens);
210 self.value.to_tokens(tokens);
211 }
212}
213
214impl PartialEq for MetaNameValueInvalidExpr {
215 fn eq(&self, other: &Self) -> bool {
216 self.path == other.path
217 && self.eq_token == other.eq_token
218 && self.value.to_string() == other.value.to_string()
219 }
220}
221
222impl Eq for MetaNameValueInvalidExpr {}
223
224impl NestedMeta {
225 pub fn parse_meta_list(tokens: TokenStream) -> syn::Result<Vec<Self>> {
226 syn::punctuated::Punctuated::<NestedMeta, Token![,]>::parse_terminated
227 .parse2(tokens)
228 .map(|punctuated| punctuated.into_iter().collect())
229 }
230}
231
232impl syn::parse::Parse for NestedMeta {
233 fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
234 if input.peek(syn::Lit) && !(input.peek(syn::LitBool) && input.peek2(syn::Token![=])) {
251 input.parse().map(Self::Lit)
252 } else if input.peek(syn::Ident::peek_any)
253 || input.peek(Token![::]) && input.peek3(syn::Ident::peek_any)
254 {
255 let path = parse_meta_path(input)?;
256 parse_meta_after_path(path, input)
257 } else {
258 Err(input.error("expected identifier or literal"))
259 }
260 }
261}
262
263impl ToTokens for NestedMeta {
264 fn to_tokens(&self, tokens: &mut TokenStream) {
265 match self {
266 NestedMeta::Meta(meta) => meta.to_tokens(tokens),
267 NestedMeta::Lit(lit) => lit.to_tokens(tokens),
268 NestedMeta::NameValueInvalidExpr(meta) => meta.to_tokens(tokens),
269 }
270 }
271}
272
273#[cfg(test)]
274mod tests {
275 use syn::parse_quote;
276
277 use super::*;
278
279 #[test]
283 fn absolute_path() {
284 let input: NestedMeta = parse_quote!(::prost::Message);
285 assert_eq!(
286 input,
287 NestedMeta::Meta(Meta::Path(parse_quote!(::prost::Message)))
288 );
289 }
290}