amplify_syn/
parsers.rs

1// Rust language amplification derive library providing multiple generic trait
2// implementations, type wrappers, derive macros and other language enhancements
3//
4// Written in 2019-2021 by
5//     Dr. Maxim Orlovsky <orlovsky@pandoracore.com>
6//
7// To the extent possible under law, the author(s) have dedicated all
8// copyright and related and neighboring rights to this software to
9// the public domain worldwide. This software is distributed without
10// any warranty.
11//
12// You should have received a copy of the MIT License
13// along with this software.
14// If not, see <https://opensource.org/licenses/MIT>.
15
16use proc_macro2::{Ident, TokenStream};
17use quote::ToTokens;
18use syn::ext::IdentExt;
19use syn::parse::{Parse, ParseBuffer, Result};
20use syn::punctuated::Punctuated;
21use syn::{Lit, Path};
22
23use crate::{ArgValue, Error};
24
25/// Drop-in replacement for [`syn::NestedMeta`], which allows to parse
26/// attributes which can have arguments made of either literal, path or
27/// [`MetaArgNameValue`] expressions.
28pub struct MetaArgList {
29    /// List of arguments
30    pub list: Punctuated<MetaArg, Token![,]>,
31}
32
33impl Parse for MetaArgList {
34    fn parse(input: &ParseBuffer) -> Result<Self> {
35        let content;
36        parenthesized!(content in input);
37        let list = Punctuated::parse_terminated(&content)?;
38        Ok(MetaArgList { list })
39    }
40}
41
42impl ToTokens for MetaArgList {
43    fn to_tokens(&self, tokens: &mut TokenStream) { (quote! { ( list ) }).to_tokens(tokens); }
44}
45
46/// Drop-in replacement for [`syn::NestedMeta`], which allows to parse
47/// attributes which can have arguments made of either literal, path or
48/// [`MetaArgNameValue`] expressions.
49pub enum MetaArg {
50    /// Attribute argument in form of literal
51    Literal(Lit),
52
53    /// Attribute argument in form of a path
54    Path(Path),
55
56    /// Attribute argument in form of `name = value` expression, where value
57    /// can be any [`ArgValue`]-representable data
58    NameValue(MetaArgNameValue),
59}
60
61impl Parse for MetaArg {
62    fn parse(input: &ParseBuffer) -> Result<Self> {
63        if input.peek2(Token![=]) {
64            input.parse().map(MetaArg::NameValue)
65        } else if input.peek(Ident::peek_any) ||
66            input.peek(Token![::]) && input.peek3(Ident::peek_any)
67        {
68            input.parse().map(MetaArg::Path)
69        } else {
70            input.parse().map(MetaArg::Literal)
71        }
72    }
73}
74
75impl ToTokens for MetaArg {
76    fn to_tokens(&self, tokens: &mut TokenStream) {
77        match self {
78            MetaArg::Literal(lit) => lit.to_tokens(tokens),
79            MetaArg::Path(path) => path.to_tokens(tokens),
80            MetaArg::NameValue(meta) => meta.to_tokens(tokens),
81        }
82    }
83}
84
85/// Drop-in replacement for [`syn::MetaNameValue`] used for parsing named
86/// arguments inside attributes which name is always an [`proc_macro2::Ident`]
87/// (and not [`syn::Path`]) and value can be not only a literal, but of any
88/// valid rust type.
89pub struct MetaArgNameValue {
90    /// Argument name
91    pub name: Ident,
92    /// Token placeholder
93    pub eq_token: Token![=],
94    /// Argument value
95    pub value: ArgValue,
96}
97
98impl Parse for MetaArgNameValue {
99    fn parse(input: &ParseBuffer) -> Result<Self> {
100        let path: Path = input.parse()?;
101        Ok(MetaArgNameValue {
102            name: path.get_ident().ok_or(Error::ArgNameMustBeIdent)?.clone(),
103            eq_token: input.parse()?,
104            value: input.parse()?,
105        })
106    }
107}
108
109impl ToTokens for MetaArgNameValue {
110    fn to_tokens(&self, tokens: &mut TokenStream) {
111        self.name.to_tokens(tokens);
112        self.eq_token.to_tokens(tokens);
113        self.value.to_tokens(tokens);
114    }
115}
116
117impl Parse for ArgValue {
118    fn parse(input: &ParseBuffer) -> Result<Self> {
119        if input.peek(Lit) {
120            input.parse().map(ArgValue::Literal)
121        } else {
122            input
123                .parse()
124                .map(ArgValue::Type)
125                .or_else(|_| input.parse().map(ArgValue::Expr))
126        }
127    }
128}
129
130impl ToTokens for ArgValue {
131    fn to_tokens(&self, tokens: &mut TokenStream) {
132        match self {
133            ArgValue::Literal(lit) => lit.to_tokens(tokens),
134            ArgValue::Type(ty) => ty.to_tokens(tokens),
135            ArgValue::Expr(expr) => expr.to_tokens(tokens),
136            ArgValue::None => quote! { ! }.to_tokens(tokens),
137        }
138    }
139}