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