tracing_gum_proc_macro/
lib.rs1#![deny(unused_crate_dependencies)]
18#![deny(missing_docs)]
19#![deny(clippy::dbg_macro)]
20
21use proc_macro2::{Ident, Span, TokenStream};
24use quote::{quote, ToTokens};
25use syn::{parse2, parse_quote, punctuated::Punctuated, Result, Token};
26
27mod types;
28
29use self::types::*;
30
31#[cfg(test)]
32mod tests;
33
34#[proc_macro]
36pub fn error(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
37 gum(item, Level::Error)
38}
39
40#[proc_macro]
42pub fn warn(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
43 gum(item, Level::Warn)
44}
45
46#[proc_macro]
48pub fn warn_if_frequent(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
49 let ArgsIfFrequent { freq, max_rate, rest } = parse2(item.into()).unwrap();
50
51 let freq_expr = freq.expr;
52 let max_rate_expr = max_rate.expr;
53 let debug: proc_macro2::TokenStream = gum(rest.clone().into(), Level::Debug).into();
54 let warn: proc_macro2::TokenStream = gum(rest.into(), Level::Warn).into();
55
56 let stream = quote! {
57 if #freq_expr .is_frequent(#max_rate_expr) {
58 #warn
59 } else {
60 #debug
61 }
62 };
63
64 stream.into()
65}
66
67#[proc_macro]
69pub fn info(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
70 gum(item, Level::Info)
71}
72
73#[proc_macro]
75pub fn debug(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
76 gum(item, Level::Debug)
77}
78
79#[proc_macro]
81pub fn trace(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
82 gum(item, Level::Trace)
83}
84
85pub(crate) fn gum(item: proc_macro::TokenStream, level: Level) -> proc_macro::TokenStream {
87 let item: TokenStream = item.into();
88
89 let res = expander::Expander::new("gum")
90 .add_comment("Generated overseer code by `gum::warn!(..)`".to_owned())
91 .dry(true)
95 .verbose(false)
96 .fmt(expander::Edition::_2021)
97 .maybe_write_to_out_dir(impl_gum2(item, level))
98 .expect("Expander does not fail due to IO in OUT_DIR. qed");
99
100 res.unwrap_or_else(|err| err.to_compile_error()).into()
101}
102
103pub(crate) fn impl_gum2(orig: TokenStream, level: Level) -> Result<TokenStream> {
107 let args: Args = parse2(orig)?;
108
109 let krate = support_crate();
110 let span = Span::call_site();
111
112 let Args { target, comma, mut values, fmt } = args;
113
114 let maybe_candidate_hash = values.iter_mut().find(|value| value.as_ident() == "candidate_hash");
116
117 if let Some(kv) = maybe_candidate_hash {
118 let (ident, rhs_expr, replace_with) = match kv {
119 Value::Alias(alias) => {
120 let ValueWithAliasIdent { alias, marker, expr, .. } = alias.clone();
121 (
122 alias.clone(),
123 expr.to_token_stream(),
124 Some(Value::Value(ValueWithFormatMarker {
125 marker,
126 ident: alias,
127 dot: None,
128 inner: Punctuated::new(),
129 })),
130 )
131 },
132 Value::Value(value) => (value.ident.clone(), value.ident.to_token_stream(), None),
133 };
134
135 if let Some(replace_with) = replace_with {
138 let _old = std::mem::replace(kv, replace_with);
139 };
140
141 let had_trailing_comma = values.trailing_punct();
144 if !had_trailing_comma {
145 values.push_punct(Token);
146 }
147
148 values.push_value(parse_quote! {
149 traceID = % trace_id
150 });
151 if had_trailing_comma {
152 values.push_punct(Token);
153 }
154
155 Ok(quote! {
156 if #krate :: enabled!(#target #comma #level) {
157 use ::std::ops::Deref;
158
159 let value = #rhs_expr;
162 let value = &value;
163 let value: & #krate:: Hash = value.deref();
164 let #ident: #krate:: Hash = * value;
166 let trace_id = #krate:: hash_to_trace_identifier ( #ident );
167 #krate :: event!(
168 #target #comma #level, #values #fmt
169 )
170 }
171 })
172 } else {
173 Ok(quote! {
174 #krate :: event!(
175 #target #comma #level, #values #fmt
176 )
177 })
178 }
179}
180
181fn support_crate() -> TokenStream {
183 let support_crate_name = if cfg!(test) {
184 quote! {crate}
185 } else {
186 use proc_macro_crate::{crate_name, FoundCrate};
187 let crate_name = crate_name("tracing-gum")
188 .expect("Support crate `tracing-gum` is present in `Cargo.toml`. qed");
189 match crate_name {
190 FoundCrate::Itself => quote! {crate},
191 FoundCrate::Name(name) => Ident::new(&name, Span::call_site()).to_token_stream(),
192 }
193 };
194 support_crate_name
195}