prometheus_client_derive_encode/
lib.rs1#![deny(dead_code)]
2#![deny(missing_docs)]
3#![deny(unused)]
4#![forbid(unsafe_code)]
5#![warn(missing_debug_implementations)]
6
7use proc_macro::TokenStream;
10use proc_macro2::TokenStream as TokenStream2;
11use quote::quote;
12use syn::DeriveInput;
13
14fn error_spanned<T: quote::ToTokens>(tokens: &T, msg: &str) -> TokenStream {
15 syn::Error::new_spanned(tokens, msg)
16 .to_compile_error()
17 .into()
18}
19
20#[proc_macro_derive(EncodeLabelSet, attributes(prometheus))]
22pub fn derive_encode_label_set(input: TokenStream) -> TokenStream {
23 let ast: DeriveInput = syn::parse(input).unwrap();
24 let name = &ast.ident;
25
26 let body: TokenStream2 = match ast.clone().data {
27 syn::Data::Struct(s) => match s.fields {
28 syn::Fields::Named(syn::FieldsNamed { named, .. }) => {
29 let body = named
30 .into_iter()
31 .map(|f| {
32 let ident = f.ident.unwrap();
33 let flatten = match f
34 .attrs
35 .iter()
36 .find(|a| a.path().is_ident("prometheus"))
37 {
38 None => false,
39 Some(a) => match a.parse_args::<syn::Ident>() {
40 Ok(ident) if ident == "flatten" => true,
41 Ok(other) => {
42 return Err(error_spanned(
43 &other,
44 &format!(
45 "Provided attribute '{other}', but only 'flatten' is supported"
46 ),
47 ));
48 }
49 Err(_) => {
50 return Err(error_spanned(
51 a,
52 "Attribute on `#[prometheus(...)]` must be an identifier, e.g. `#[prometheus(flatten)]`",
53 ));
54 }
55 },
56 };
57
58 if flatten {
59 Ok(quote! {
60 EncodeLabelSet::encode(&self.#ident, encoder)?;
61 })
62 } else {
63 let ident_string = KEYWORD_IDENTIFIERS
64 .iter()
65 .find(|pair| ident == pair.1)
66 .map(|pair| pair.0.to_string())
67 .unwrap_or_else(|| ident.to_string());
68
69 Ok(quote! {
70 let mut label_encoder = encoder.encode_label();
71 let mut label_key_encoder = label_encoder.encode_label_key()?;
72 EncodeLabelKey::encode(&#ident_string, &mut label_key_encoder)?;
73
74 let mut label_value_encoder = label_key_encoder.encode_label_value()?;
75 EncodeLabelValue::encode(&self.#ident, &mut label_value_encoder)?;
76
77 label_value_encoder.finish()?;
78 })
79 }
80 })
81 .collect::<Result<TokenStream2, TokenStream>>();
82
83 match body {
84 Ok(body) => body,
85 Err(err) => return err,
86 }
87 }
88 syn::Fields::Unnamed(_) => {
89 return error_spanned(
90 &ast,
91 "Can not derive Encode for struct with unnamed fields.",
92 );
93 }
94 syn::Fields::Unit => {
95 return error_spanned(&ast, "Can not derive Encode for struct with unit field.");
96 }
97 },
98 syn::Data::Enum(syn::DataEnum { .. }) => {
99 return error_spanned(&ast, "Can not derive Encode for enum.");
100 }
101 syn::Data::Union(_) => {
102 return error_spanned(&ast, "Can not derive Encode for union.");
103 }
104 };
105
106 let gen = quote! {
107 impl ::prometheus_client::encoding::EncodeLabelSet for #name {
108 fn encode(&self, encoder: &mut ::prometheus_client::encoding::LabelSetEncoder) -> ::core::result::Result<(), ::core::fmt::Error> {
109 use ::prometheus_client::encoding::EncodeLabel;
110 use ::prometheus_client::encoding::EncodeLabelKey;
111 use ::prometheus_client::encoding::EncodeLabelValue;
112
113 #body
114
115 ::core::result::Result::Ok(())
116 }
117 }
118 };
119
120 gen.into()
121}
122
123#[proc_macro_derive(EncodeLabelValue)]
125pub fn derive_encode_label_value(input: TokenStream) -> TokenStream {
126 let ast: DeriveInput = syn::parse(input).unwrap();
127 let name = &ast.ident;
128
129 let body = match ast.clone().data {
130 syn::Data::Struct(_) => {
131 return error_spanned(&ast, "Can not derive EncodeLabel for struct.");
132 }
133 syn::Data::Enum(syn::DataEnum { variants, .. }) => {
134 let match_arms: TokenStream2 = variants
135 .into_iter()
136 .map(|v| {
137 let ident = v.ident;
138 quote! {
139 #name::#ident => encoder.write_str(stringify!(#ident))?,
140 }
141 })
142 .collect();
143
144 quote! {
145 match self {
146 #match_arms
147 }
148 }
149 }
150 syn::Data::Union(_) => {
151 return error_spanned(&ast, "Can not derive Encode for union.");
152 }
153 };
154
155 let gen = quote! {
156 impl ::prometheus_client::encoding::EncodeLabelValue for #name {
157 fn encode(&self, encoder: &mut ::prometheus_client::encoding::LabelValueEncoder) -> ::core::result::Result<(), ::core::fmt::Error> {
158 use ::core::fmt::Write;
159
160 #body
161
162 ::core::result::Result::Ok(())
163 }
164 }
165 };
166
167 gen.into()
168}
169
170static KEYWORD_IDENTIFIERS: [(&str, &str); 48] = [
173 ("as", "r#as"),
174 ("break", "r#break"),
175 ("const", "r#const"),
176 ("continue", "r#continue"),
177 ("crate", "r#crate"),
178 ("else", "r#else"),
179 ("enum", "r#enum"),
180 ("extern", "r#extern"),
181 ("false", "r#false"),
182 ("fn", "r#fn"),
183 ("for", "r#for"),
184 ("if", "r#if"),
185 ("impl", "r#impl"),
186 ("in", "r#in"),
187 ("let", "r#let"),
188 ("loop", "r#loop"),
189 ("match", "r#match"),
190 ("mod", "r#mod"),
191 ("move", "r#move"),
192 ("mut", "r#mut"),
193 ("pub", "r#pub"),
194 ("ref", "r#ref"),
195 ("return", "r#return"),
196 ("static", "r#static"),
197 ("struct", "r#struct"),
198 ("trait", "r#trait"),
199 ("true", "r#true"),
200 ("type", "r#type"),
201 ("unsafe", "r#unsafe"),
202 ("use", "r#use"),
203 ("where", "r#where"),
204 ("while", "r#while"),
205 ("async", "r#async"),
206 ("await", "r#await"),
207 ("dyn", "r#dyn"),
208 ("abstract", "r#abstract"),
209 ("become", "r#become"),
210 ("box", "r#box"),
211 ("do", "r#do"),
212 ("final", "r#final"),
213 ("macro", "r#macro"),
214 ("override", "r#override"),
215 ("priv", "r#priv"),
216 ("typeof", "r#typeof"),
217 ("unsized", "r#unsized"),
218 ("virtual", "r#virtual"),
219 ("yield", "r#yield"),
220 ("try", "r#try"),
221];