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::parse::{Parse, ParseStream, Parser};
31use syn::punctuated::Punctuated;
32use syn::{spanned::Spanned, Attribute, Error, LitStr, Token};
33
34pub(crate) struct AttributeMeta {
35	pub path: syn::Path,
36	pub arguments: Punctuated<Argument, Token![,]>,
37}
38
39pub(crate) struct Argument {
40	pub label: syn::Ident,
41	pub tokens: TokenStream2,
42}
43
44#[derive(Debug, Clone)]
45pub enum ParamKind {
46	Array,
47	Map,
48}
49
50pub struct NameMapping {
51	pub name: String,
52	pub mapped: Option<String>,
53}
54
55pub struct Bracketed<T> {
56	pub list: Punctuated<T, Token![,]>,
57}
58
59pub type Aliases = Bracketed<LitStr>;
60
61impl Parse for Argument {
62	fn parse(input: ParseStream) -> syn::Result<Self> {
63		let label = input.parse()?;
64
65		let mut scope = 0usize;
66
67		// Need to read to till either the end of the stream,
68		// or the nearest comma token that's not contained
69		// inside angle brackets.
70		let tokens = iter::from_fn(move || {
71			if scope == 0 && input.peek(Token![,]) {
72				return None;
73			}
74
75			if input.peek(Token![<]) {
76				scope += 1;
77			} else if input.peek(Token![>]) {
78				scope = scope.saturating_sub(1);
79			}
80
81			input.parse::<TokenTree>().ok()
82		})
83		.collect();
84
85		Ok(Argument { label, tokens })
86	}
87}
88
89impl Parse for NameMapping {
90	fn parse(input: ParseStream) -> syn::Result<Self> {
91		let name = input.parse::<LitStr>()?.value();
92
93		let mapped = if input.peek(Token![=>]) {
94			input.parse::<Token![=>]>()?;
95
96			Some(input.parse::<LitStr>()?.value())
97		} else {
98			None
99		};
100
101		Ok(NameMapping { name, mapped })
102	}
103}
104
105impl<T: Parse> Parse for Bracketed<T> {
106	fn parse(input: ParseStream) -> syn::Result<Self> {
107		let content;
108
109		syn::bracketed!(content in input);
110
111		let list = content.parse_terminated(Parse::parse, Token![,])?;
112
113		Ok(Bracketed { list })
114	}
115}
116
117fn parenthesized<T: Parse>(input: ParseStream) -> syn::Result<Punctuated<T, Token![,]>> {
118	let content;
119
120	syn::parenthesized!(content in input);
121
122	content.parse_terminated(T::parse, Token![,])
123}
124
125impl AttributeMeta {
126	/// Parses `Attribute` with plain `TokenStream` into a more robust `AttributeMeta` with
127	/// a collection `Arguments`.
128	pub fn parse(attr: Attribute) -> syn::Result<AttributeMeta> {
129		let path = attr.path().clone();
130
131		let arguments = attr.parse_args_with(|input: ParseStream| input.parse_terminated(Parse::parse, Token![,]))?;
132
133		Ok(AttributeMeta { path, arguments })
134	}
135
136	/// Attempt to get a list of `Argument`s from a list of names in order.
137	///
138	/// Errors if there is an argument with a name that's not on the list, or if there is a duplicate definition.
139	pub fn retain<const N: usize>(self, allowed: [&str; N]) -> syn::Result<[Result<Argument, MissingArgument>; N]> {
140		assert!(
141			N != 0,
142			"Calling `AttributeMeta::retain` with an empty `allowed` list, this is a bug, please report it"
143		);
144
145		let mut result: [Result<Argument, _>; N] = allowed.map(|name| Err(MissingArgument(self.path.span(), name)));
146
147		for argument in self.arguments {
148			if let Some(idx) = allowed.iter().position(|probe| argument.label == probe) {
149				// If this position in the `result` array already contains an argument,
150				// it means we got a duplicate definition
151				if let Ok(old) = &result[idx] {
152					return Err(Error::new(old.label.span(), format!("Duplicate argument `{}`", old.label)));
153				}
154
155				result[idx] = Ok(argument);
156			} else {
157				let mut err_str = format!("Unknown argument `{}`, expected one of: `", &argument.label);
158
159				err_str.push_str(allowed[0]);
160				err_str.extend(allowed[1..].iter().flat_map(|&label| ["`, `", label]));
161				err_str.push('`');
162
163				return Err(Error::new(argument.label.span(), err_str));
164			}
165		}
166
167		Ok(result)
168	}
169}
170
171pub(crate) struct MissingArgument<'a>(Span, &'a str);
172
173impl fmt::Display for MissingArgument<'_> {
174	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175		let MissingArgument(_, missing) = self;
176
177		write!(f, "Missing argument `{missing}`")
178	}
179}
180
181impl From<MissingArgument<'_>> for Error {
182	fn from(missing: MissingArgument) -> Self {
183		Error::new(missing.0, missing)
184	}
185}
186
187impl Argument {
188	/// Asserts that the argument is just a simple `flag` without any value present
189	pub fn flag(self) -> syn::Result<()> {
190		if self.tokens.is_empty() {
191			Ok(())
192		} else {
193			Err(Error::new(self.tokens.span(), "Expected a flag argument"))
194		}
195	}
196
197	/// Asserts that the argument is `key = value` pair and parses the value into `T`
198	pub fn value<T: Parse>(self) -> syn::Result<T> {
199		fn value_parser<T: Parse>(stream: ParseStream) -> syn::Result<T> {
200			stream.parse::<Token![=]>()?;
201			stream.parse()
202		}
203
204		value_parser.parse2(self.tokens)
205	}
206
207	pub fn group<T>(self) -> syn::Result<Punctuated<T, Token![,]>>
208	where
209		T: Parse,
210	{
211		parenthesized.parse2(self.tokens)
212	}
213
214	/// Asserts that the argument is `key = "string"` and gets the value of the string
215	pub fn string(self) -> syn::Result<String> {
216		self.value::<LitStr>().map(|lit| lit.value())
217	}
218}
219
220pub(crate) fn optional<T, F>(arg: Result<Argument, MissingArgument>, transform: F) -> syn::Result<Option<T>>
221where
222	F: Fn(Argument) -> syn::Result<T>,
223{
224	arg.ok().map(transform).transpose()
225}
226
227pub(crate) fn parse_param_kind(arg: Result<Argument, MissingArgument>) -> syn::Result<ParamKind> {
228	let kind: Option<syn::Ident> = optional(arg, Argument::value)?;
229
230	match kind {
231		None => Ok(ParamKind::Array),
232		Some(ident) if ident == "array" => Ok(ParamKind::Array),
233		Some(ident) if ident == "map" => Ok(ParamKind::Map),
234		ident => Err(Error::new(ident.span(), "param_kind must be either `map` or `array`")),
235	}
236}