xaynet_macros/lib.rs
1extern crate proc_macro;
2
3use proc_macro::TokenStream;
4use quote::quote;
5use syn::{
6 parse::{Parse, ParseStream, Result},
7 parse_macro_input,
8 Expr,
9 Token,
10};
11struct Send {
12 sender: Expr,
13 metrics: Vec<Expr>,
14}
15
16impl Parse for Send {
17 fn parse(input: ParseStream) -> Result<Self> {
18 // metrics!(sender, metric_1);
19 let sender = input.parse()?; // sender
20 let mut metrics = Vec::new();
21
22 // at least one metric is required, otherwise parse will fail.
23 input.parse::<Token![,]>()?; // ,
24 let metric = input.parse()?; // metric_1
25 metrics.push(metric);
26
27 // metrics!(sender, metric_1, metric_N);
28 loop {
29 if input.is_empty() {
30 break;
31 }
32
33 input.parse::<Token![,]>()?; // ,
34 let metric = input.parse()?; // metrics_N
35
36 metrics.push(metric);
37 }
38
39 Ok(Send { sender, metrics })
40 }
41}
42
43/// Allows one or multiple metrics to be sent through a `Sender` when the `metrics` feature is
44/// enabled.
45///
46/// The idea is to only include the code for sending metrics if the `metrics` feature flag is enabled
47/// during compilation. This can be achieved through conditional compilation, more precisely with the
48/// attribute `cfg`.
49///
50/// See [here](https://www.worthe-it.co.za/programming/2018/11/18/compile-time-feature-flags-in-rust.html)
51/// for more information about conditional compilation in Rust.
52///
53/// This macro helps to reduce the usage of the attribute `#[cfg(feature = "metrics")]` within the
54/// source code.
55///
56/// ## Macro arguments:
57///
58/// `metrics!(sender, metric_1, metric_2, metric_N)`
59///
60/// ## Basic usage:
61///
62/// ```ignore
63/// fn main() {
64/// metrics!(sender, metrics::round::total_number::update(1));
65///
66/// metrics!(
67/// sender,
68/// metrics::round::total_number::update(1),
69/// metrics::masks::total_number::update(1, 1, PhaseName::Idle)
70/// );
71/// }
72/// ```
73///
74/// Equivalent code not using `metrics!`
75///
76/// ```ignore
77/// fn main() {
78/// #[cfg(feature = "metrics")]
79/// {
80/// sender.send(metrics::round::total_number::update(1)),
81/// };
82///
83/// #[cfg(feature = "metrics")]
84/// {
85/// sender.send(metrics::round::total_number::update(1)),
86/// sender.send(metrics::masks::total_number::update(1, 1, PhaseName::Idle)),
87/// };
88/// }
89/// ```
90///
91/// ## Sender
92///
93/// A `Sender` must implement the method `pub fn send(&self, metrics: T)` where `T`
94/// is the type of the metric.
95///
96/// ### Example of a `Sender` implementation
97///
98/// ```ignore
99/// use influxdb::WriteQuery;
100/// use tokio::sync::mpsc::Sender;
101///
102/// pub struct MetricsSender(Sender<WriteQuery>);
103///
104/// impl MetricsSender {
105/// pub fn send(&mut self, query: WriteQuery) {
106/// let _ = self.0.try_send(query).map_err(|e| error!("{}", e));
107/// }
108/// }
109/// ```
110#[proc_macro]
111pub fn metrics(input: TokenStream) -> TokenStream {
112 let Send { sender, metrics } = parse_macro_input!(input as Send);
113
114 TokenStream::from(quote! {
115 #[cfg(feature = "metrics")]
116 {
117 #(#sender.send(#metrics);)*
118 }
119 })
120}