referrerpolicy=no-referrer-when-downgrade

tracing_gum_proc_macro/
types.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Polkadot.
3
4// Polkadot is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Polkadot is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Polkadot.  If not, see <http://www.gnu.org/licenses/>.
16
17use super::*;
18
19use syn::{
20	parse::{Parse, ParseStream},
21	Token,
22};
23
24pub(crate) mod kw {
25	syn::custom_keyword!(target);
26	syn::custom_keyword!(freq);
27	syn::custom_keyword!(max_rate);
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub(crate) struct Target {
32	kw: kw::target,
33	colon: Token![:],
34	expr: syn::Expr,
35}
36
37impl Parse for Target {
38	fn parse(input: ParseStream) -> Result<Self> {
39		Ok(Self { kw: input.parse()?, colon: input.parse()?, expr: input.parse()? })
40	}
41}
42
43impl ToTokens for Target {
44	fn to_tokens(&self, tokens: &mut TokenStream) {
45		let kw = &self.kw;
46		let colon = &self.colon;
47		let expr = &self.expr;
48		tokens.extend(quote! {
49			#kw #colon #expr
50		})
51	}
52}
53
54#[derive(Debug, Clone, Copy, PartialEq, Eq)]
55pub(crate) enum FormatMarker {
56	Questionmark(Token![?]),
57	Percentage(Token![%]),
58	None,
59}
60
61impl Parse for FormatMarker {
62	fn parse(input: ParseStream) -> Result<Self> {
63		let lookahead = input.lookahead1();
64		if lookahead.peek(Token![?]) {
65			input.parse().map(Self::Questionmark)
66		} else if lookahead.peek(Token![%]) {
67			input.parse().map(Self::Percentage)
68		} else {
69			Ok(Self::None)
70		}
71	}
72}
73
74impl ToTokens for FormatMarker {
75	fn to_tokens(&self, tokens: &mut TokenStream) {
76		tokens.extend(match self {
77			Self::Percentage(p) => p.to_token_stream(),
78			Self::Questionmark(q) => q.to_token_stream(),
79			Self::None => TokenStream::new(),
80		})
81	}
82}
83
84#[derive(Debug, Clone, PartialEq, Eq)]
85pub(crate) struct ValueWithAliasIdent {
86	pub alias: Ident,
87	pub eq: Token![=],
88	pub marker: FormatMarker,
89	pub expr: syn::Expr,
90}
91
92impl Parse for ValueWithAliasIdent {
93	fn parse(input: ParseStream) -> Result<Self> {
94		Ok(Self {
95			alias: input.parse()?,
96			eq: input.parse()?,
97			marker: input.parse()?,
98			expr: input.parse()?,
99		})
100	}
101}
102
103impl ToTokens for ValueWithAliasIdent {
104	fn to_tokens(&self, tokens: &mut TokenStream) {
105		let alias = &self.alias;
106		let eq = &self.eq;
107		let marker = &self.marker;
108		let expr = &self.expr;
109		tokens.extend(quote! {
110			#alias #eq #marker #expr
111		})
112	}
113}
114
115#[derive(Debug, Clone, PartialEq, Eq)]
116
117pub(crate) struct ValueWithFormatMarker {
118	pub marker: FormatMarker,
119	pub ident: Ident,
120	pub dot: Option<Token![.]>,
121	pub inner: Punctuated<syn::Member, Token![.]>,
122}
123
124impl Parse for ValueWithFormatMarker {
125	fn parse(input: ParseStream) -> Result<Self> {
126		let marker = input.parse::<FormatMarker>()?;
127		let ident = input.parse::<syn::Ident>()?;
128
129		let mut inner = Punctuated::<syn::Member, Token![.]>::new();
130
131		let lookahead = input.lookahead1();
132		let dot = if lookahead.peek(Token![.]) {
133			let dot = Some(input.parse::<Token![.]>()?);
134
135			loop {
136				let member = input.parse::<syn::Member>()?;
137				inner.push_value(member);
138
139				let lookahead = input.lookahead1();
140				if !lookahead.peek(Token![.]) {
141					break
142				}
143
144				let token = input.parse::<Token![.]>()?;
145				inner.push_punct(token);
146			}
147
148			dot
149		} else {
150			None
151		};
152		Ok(Self { marker, ident, dot, inner })
153	}
154}
155
156impl ToTokens for ValueWithFormatMarker {
157	fn to_tokens(&self, tokens: &mut TokenStream) {
158		let marker = &self.marker;
159		let ident = &self.ident;
160		let dot = &self.dot;
161		let inner = &self.inner;
162		tokens.extend(quote! {
163			#marker #ident #dot #inner
164		})
165	}
166}
167
168/// A value as passed to the macro, appearing _before_ the format string.
169#[derive(Debug, Clone, PartialEq, Eq)]
170
171pub(crate) enum Value {
172	Alias(ValueWithAliasIdent),
173	Value(ValueWithFormatMarker),
174}
175
176impl Value {
177	pub fn as_ident(&self) -> &Ident {
178		match self {
179			Self::Alias(alias) => &alias.alias,
180			Self::Value(value) => &value.ident,
181		}
182	}
183}
184
185impl Parse for Value {
186	fn parse(input: ParseStream) -> Result<Self> {
187		if input.fork().parse::<ValueWithAliasIdent>().is_ok() {
188			input.parse().map(Self::Alias)
189		} else if input.fork().parse::<ValueWithFormatMarker>().is_ok() {
190			input.parse().map(Self::Value)
191		} else {
192			Err(syn::Error::new(Span::call_site(), "Neither value nor aliased value."))
193		}
194	}
195}
196
197impl ToTokens for Value {
198	fn to_tokens(&self, tokens: &mut TokenStream) {
199		tokens.extend(match self {
200			Self::Alias(alias) => quote! { #alias },
201			Self::Value(value) => quote! { #value },
202		})
203	}
204}
205
206/// Defines the token stream consisting of a format string and it's arguments.
207///
208/// Attention: Currently the correctness of the arguments is not checked as part
209/// of the parsing logic.
210/// It would be possible to use `parse_fmt_str:2.0.0`
211/// to do so and possibly improve the error message here - for the time being
212/// it's not clear if this yields any practical benefits, and is hence
213/// left for future consideration.
214#[derive(Debug, Clone)]
215pub(crate) struct FmtGroup {
216	pub format_str: syn::LitStr,
217	pub maybe_comma: Option<Token![,]>,
218	pub rest: TokenStream,
219}
220
221impl Parse for FmtGroup {
222	fn parse(input: ParseStream) -> Result<Self> {
223		let format_str = input
224			.parse()
225			.map_err(|e| syn::Error::new(e.span(), "Expected format specifier"))?;
226
227		let (maybe_comma, rest) = if input.peek(Token![,]) {
228			let comma = input.parse::<Token![,]>()?;
229			let rest = input.parse()?;
230			(Some(comma), rest)
231		} else {
232			(None, TokenStream::new())
233		};
234
235		if !input.is_empty() {
236			return Err(syn::Error::new(input.span(), "Unexpected data, expected closing `)`."))
237		}
238
239		Ok(Self { format_str, maybe_comma, rest })
240	}
241}
242
243impl ToTokens for FmtGroup {
244	fn to_tokens(&self, tokens: &mut TokenStream) {
245		let format_str = &self.format_str;
246		let maybe_comma = &self.maybe_comma;
247		let rest = &self.rest;
248
249		tokens.extend(quote! { #format_str #maybe_comma #rest });
250	}
251}
252
253#[derive(Debug, Clone, PartialEq, Eq)]
254pub(crate) struct Freq {
255	kw: kw::freq,
256	colon: Token![:],
257	pub expr: syn::Expr,
258}
259
260impl Parse for Freq {
261	fn parse(input: ParseStream) -> Result<Self> {
262		Ok(Self { kw: input.parse()?, colon: input.parse()?, expr: input.parse()? })
263	}
264}
265
266#[derive(Debug, Clone, PartialEq, Eq)]
267pub(crate) struct MaxRate {
268	kw: kw::max_rate,
269	colon: Token![:],
270	pub expr: syn::Expr,
271}
272
273impl Parse for MaxRate {
274	fn parse(input: ParseStream) -> Result<Self> {
275		Ok(Self { kw: input.parse()?, colon: input.parse()?, expr: input.parse()? })
276	}
277}
278
279pub(crate) struct ArgsIfFrequent {
280	pub freq: Freq,
281	pub max_rate: MaxRate,
282	pub rest: TokenStream,
283}
284
285impl Parse for ArgsIfFrequent {
286	fn parse(input: ParseStream) -> Result<Self> {
287		let freq = input.parse()?;
288		let _: Token![,] = input.parse()?;
289		let max_rate = input.parse()?;
290		let _: Token![,] = input.parse()?;
291		let rest = input.parse()?;
292
293		Ok(Self { freq, max_rate, rest })
294	}
295}
296
297/// Full set of arguments as provided to the `gum::warn!` call.
298#[derive(Debug, Clone)]
299pub(crate) struct Args {
300	pub target: Option<Target>,
301	pub comma: Option<Token![,]>,
302	pub values: Punctuated<Value, Token![,]>,
303	pub fmt: Option<FmtGroup>,
304}
305
306impl Parse for Args {
307	fn parse(input: ParseStream) -> Result<Self> {
308		let lookahead = input.lookahead1();
309		let (target, comma) = if lookahead.peek(kw::target) {
310			let target = input.parse()?;
311			let comma = input.parse::<Token![,]>()?;
312			(Some(target), Some(comma))
313		} else {
314			(None, None)
315		};
316
317		let mut values = Punctuated::new();
318		loop {
319			if input.fork().parse::<Value>().is_ok() {
320				values.push_value(input.parse::<Value>()?);
321			} else {
322				break
323			}
324			if input.peek(Token![,]) {
325				values.push_punct(input.parse::<Token![,]>()?);
326			} else {
327				break
328			}
329		}
330
331		let fmt = if values.empty_or_trailing() && !input.is_empty() {
332			let fmt = input.parse::<FmtGroup>()?;
333			Some(fmt)
334		} else {
335			None
336		};
337
338		Ok(Self { target, comma, values, fmt })
339	}
340}
341
342impl ToTokens for Args {
343	fn to_tokens(&self, tokens: &mut TokenStream) {
344		let target = &self.target;
345		let comma = &self.comma;
346		let values = &self.values;
347		let fmt = &self.fmt;
348		tokens.extend(quote! {
349			#target #comma #values #fmt
350		})
351	}
352}
353
354/// Support tracing levels, passed to `tracing::event!`
355///
356/// Note: Not parsed from the input stream, but implicitly defined
357/// by the macro name, i.e. `level::debug!` is `Level::Debug`.
358#[derive(Debug, Clone, Copy, PartialEq, Eq)]
359pub(crate) enum Level {
360	Error,
361	Warn,
362	Info,
363	Debug,
364	Trace,
365}
366
367impl ToTokens for Level {
368	fn to_tokens(&self, tokens: &mut TokenStream) {
369		let span = Span::call_site();
370		let variant = match self {
371			Self::Error => Ident::new("ERROR", span),
372			Self::Warn => Ident::new("WARN", span),
373			Self::Info => Ident::new("INFO", span),
374			Self::Debug => Ident::new("DEBUG", span),
375			Self::Trace => Ident::new("TRACE", span),
376		};
377		let krate = support_crate();
378		tokens.extend(quote! {
379			#krate :: Level :: #variant
380		})
381	}
382}