Skip to main content

jsonrpsee_proc_macros/
attributes.rs

1// Copyright 2019-2021 Parity Technologies (UK) Ltd.
2//
3// Permission is hereby granted, free of charge, to any
4// person obtaining a copy of this software and associated
5// documentation files (the "Software"), to deal in the
6// Software without restriction, including without
7// limitation the rights to use, copy, modify, merge,
8// publish, distribute, sublicense, and/or sell copies of
9// the Software, and to permit persons to whom the Software
10// is furnished to do so, subject to the following
11// conditions:
12//
13// The above copyright notice and this permission notice
14// shall be included in all copies or substantial portions
15// of the Software.
16//
17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24// IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25// DEALINGS IN THE SOFTWARE.
26
27use std::{fmt, iter};
28
29use proc_macro2::{Span, TokenStream as TokenStream2, TokenTree};
30use syn::{
31    parse::{Parse, ParseStream, Parser},
32    punctuated::Punctuated,
33    spanned::Spanned,
34    Attribute, Error, LitStr, Token,
35};
36
37pub(crate) struct AttributeMeta {
38    pub path: syn::Path,
39    pub arguments: Punctuated<Argument, Token![,]>,
40}
41
42pub(crate) struct Argument {
43    pub label: syn::Ident,
44    pub tokens: TokenStream2,
45}
46
47pub struct NameMapping {
48    pub name: String,
49    pub mapped: Option<String>,
50}
51
52pub struct Bracketed<T> {
53    pub list: Punctuated<T, Token![,]>,
54}
55
56pub type Aliases = Bracketed<LitStr>;
57
58impl Parse for Argument {
59    fn parse(input: ParseStream) -> syn::Result<Self> {
60        let label = input.parse()?;
61
62        let mut scope = 0usize;
63
64        // Need to read to till either the end of the stream,
65        // or the nearest comma token that's not contained
66        // inside angle brackets.
67        let tokens = iter::from_fn(move || {
68            if scope == 0 && input.peek(Token![,]) {
69                return None;
70            }
71
72            if input.peek(Token![<]) {
73                scope += 1;
74            } else if input.peek(Token![>]) {
75                scope = scope.saturating_sub(1);
76            }
77
78            input.parse::<TokenTree>().ok()
79        })
80        .collect();
81
82        Ok(Argument { label, tokens })
83    }
84}
85
86impl Parse for NameMapping {
87    fn parse(input: ParseStream) -> syn::Result<Self> {
88        let name = input.parse::<LitStr>()?.value();
89
90        let mapped = if input.peek(Token![=>]) {
91            input.parse::<Token![=>]>()?;
92
93            Some(input.parse::<LitStr>()?.value())
94        } else {
95            None
96        };
97
98        Ok(NameMapping { name, mapped })
99    }
100}
101
102impl<T: Parse> Parse for Bracketed<T> {
103    fn parse(input: ParseStream) -> syn::Result<Self> {
104        let content;
105
106        syn::bracketed!(content in input);
107
108        let list = content.parse_terminated(Parse::parse, Token![,])?;
109
110        Ok(Bracketed { list })
111    }
112}
113
114fn parenthesized<T: Parse>(input: ParseStream) -> syn::Result<Punctuated<T, Token![,]>> {
115    let content;
116
117    syn::parenthesized!(content in input);
118
119    content.parse_terminated(T::parse, Token![,])
120}
121
122impl AttributeMeta {
123    /// Parses `Attribute` with plain `TokenStream` into a more robust `AttributeMeta` with
124    /// a collection `Arguments`.
125    pub fn parse(attr: Attribute) -> syn::Result<AttributeMeta> {
126        let path = attr.path().clone();
127
128        let arguments = attr.parse_args_with(|input: ParseStream| {
129            input.parse_terminated(Parse::parse, Token![,])
130        })?;
131
132        Ok(AttributeMeta { path, arguments })
133    }
134
135    /// Attempt to get a list of `Argument`s from a list of names in order.
136    ///
137    /// Errors if there is an argument with a name that's not on the list, or if there is a
138    /// duplicate definition.
139    pub fn retain<const N: usize>(
140        self,
141        allowed: [&str; N],
142    ) -> syn::Result<[Result<Argument, MissingArgument<'_>>; N]> {
143        assert!(
144            N != 0,
145            "Calling `AttributeMeta::retain` with an empty `allowed` list, this is a bug, please report it"
146        );
147
148        let mut result: [Result<Argument, _>; N] =
149            allowed.map(|name| Err(MissingArgument(self.path.span(), name)));
150
151        for argument in self.arguments {
152            if let Some(idx) = allowed.iter().position(|probe| argument.label == probe) {
153                // If this position in the `result` array already contains an argument,
154                // it means we got a duplicate definition
155                if let Ok(old) = &result[idx] {
156                    return Err(Error::new(
157                        old.label.span(),
158                        format!("Duplicate argument `{}`", old.label),
159                    ));
160                }
161
162                result[idx] = Ok(argument);
163            } else {
164                let mut err_str =
165                    format!("Unknown argument `{}`, expected one of: `", argument.label);
166
167                err_str.push_str(allowed[0]);
168                err_str.extend(allowed[1..].iter().flat_map(|&label| ["`, `", label]));
169                err_str.push('`');
170
171                return Err(Error::new(argument.label.span(), err_str));
172            }
173        }
174
175        Ok(result)
176    }
177}
178
179pub(crate) struct MissingArgument<'a>(Span, &'a str);
180
181impl fmt::Display for MissingArgument<'_> {
182    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183        let MissingArgument(_, missing) = self;
184
185        write!(f, "Missing argument `{missing}`")
186    }
187}
188
189impl From<MissingArgument<'_>> for Error {
190    fn from(missing: MissingArgument) -> Self {
191        Error::new(missing.0, missing)
192    }
193}
194
195impl Argument {
196    /// Asserts that the argument is just a simple `flag` without any value present
197    pub fn flag(self) -> syn::Result<()> {
198        if self.tokens.is_empty() {
199            Ok(())
200        } else {
201            Err(Error::new(self.tokens.span(), "Expected a flag argument"))
202        }
203    }
204
205    /// Asserts that the argument is `key = value` pair and parses the value into `T`
206    pub fn value<T: Parse>(self) -> syn::Result<T> {
207        fn value_parser<T: Parse>(stream: ParseStream) -> syn::Result<T> {
208            stream.parse::<Token![=]>()?;
209            stream.parse()
210        }
211
212        value_parser.parse2(self.tokens)
213    }
214
215    pub fn group<T>(self) -> syn::Result<Punctuated<T, Token![,]>>
216    where
217        T: Parse,
218    {
219        parenthesized.parse2(self.tokens)
220    }
221
222    /// Asserts that the argument is `key = "string"` and gets the value of the string
223    pub fn string(self) -> syn::Result<String> {
224        self.value::<LitStr>().map(|lit| lit.value())
225    }
226}
227
228pub(crate) fn optional<T, F>(
229    arg: Result<Argument, MissingArgument>,
230    transform: F,
231) -> syn::Result<Option<T>>
232where
233    F: Fn(Argument) -> syn::Result<T>,
234{
235    arg.ok().map(transform).transpose()
236}