9
9
use proc_macro:: TokenStream ;
10
10
use proc_macro2:: TokenStream as TokenStream2 ;
11
11
use quote:: quote;
12
- use syn:: { DeriveInput , Ident } ;
12
+ use syn:: { parse :: Parse , DeriveInput , Ident , LitStr , Token } ;
13
13
14
14
/// Derive `prometheus_client::encoding::EncodeLabelSet`.
15
15
#[ proc_macro_derive( EncodeLabelSet , attributes( prometheus) ) ]
@@ -87,18 +87,27 @@ pub fn derive_encode_label_set(input: TokenStream) -> TokenStream {
87
87
gen. into ( )
88
88
}
89
89
90
- enum ValueCase {
91
- Lower ,
92
- Upper ,
93
- NoChange ,
94
- }
95
-
96
90
/// Derive `prometheus_client::encoding::EncodeLabelValue`.
97
91
#[ proc_macro_derive( EncodeLabelValue , attributes( prometheus) ) ]
98
92
pub fn derive_encode_label_value ( input : TokenStream ) -> TokenStream {
99
93
let ast: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
100
94
let name = & ast. ident ;
101
95
96
+ let config: LabelConfig = ast
97
+ . attrs
98
+ . iter ( )
99
+ . find_map ( |attr| {
100
+ if attr. path . is_ident ( "prometheus" ) {
101
+ match attr. parse_args :: < LabelConfig > ( ) {
102
+ Ok ( config) => Some ( config) ,
103
+ Err ( e) => panic ! ( "invalid prometheus attribute: {e}" ) ,
104
+ }
105
+ } else {
106
+ None
107
+ }
108
+ } )
109
+ . unwrap_or_default ( ) ;
110
+
102
111
let body = match ast. clone ( ) . data {
103
112
syn:: Data :: Struct ( _) => {
104
113
panic ! ( "Can not derive EncodeLabelValue for struct." )
@@ -120,20 +129,10 @@ pub fn derive_encode_label_value(input: TokenStream) -> TokenStream {
120
129
Some ( other) => {
121
130
panic ! ( "Provided attribute '{other}', but only 'lower' and 'upper' are supported" )
122
131
}
123
- None => ValueCase :: NoChange ,
132
+ None => config . value_case . clone ( ) ,
124
133
} ;
125
134
126
- let value = match case {
127
- ValueCase :: Lower => {
128
- Ident :: new ( & ident. to_string ( ) . to_lowercase ( ) , ident. span ( ) )
129
- } ,
130
- ValueCase :: Upper => {
131
- Ident :: new ( & ident. to_string ( ) . to_uppercase ( ) , ident. span ( ) )
132
- } ,
133
- ValueCase :: NoChange => {
134
- ident. clone ( )
135
- }
136
- } ;
135
+ let value = case. apply ( & ident) ;
137
136
138
137
quote ! {
139
138
#name:: #ident => encoder. write_str( stringify!( #value) ) ?,
@@ -165,6 +164,77 @@ pub fn derive_encode_label_value(input: TokenStream) -> TokenStream {
165
164
gen. into ( )
166
165
}
167
166
167
+ #[ derive( Clone ) ]
168
+ enum ValueCase {
169
+ Lower ,
170
+ Upper ,
171
+ NoChange ,
172
+ }
173
+
174
+ impl ValueCase {
175
+ fn apply ( & self , ident : & Ident ) -> Ident {
176
+ match self {
177
+ ValueCase :: Lower => Ident :: new ( & ident. to_string ( ) . to_lowercase ( ) , ident. span ( ) ) ,
178
+ ValueCase :: Upper => Ident :: new ( & ident. to_string ( ) . to_uppercase ( ) , ident. span ( ) ) ,
179
+ ValueCase :: NoChange => ident. clone ( ) ,
180
+ }
181
+ }
182
+ }
183
+
184
+ struct LabelConfig {
185
+ value_case : ValueCase ,
186
+ }
187
+
188
+ impl Default for LabelConfig {
189
+ fn default ( ) -> Self {
190
+ Self {
191
+ value_case : ValueCase :: NoChange ,
192
+ }
193
+ }
194
+ }
195
+
196
+ impl Parse for LabelConfig {
197
+ fn parse ( input : syn:: parse:: ParseStream ) -> syn:: Result < Self > {
198
+ let mut config = LabelConfig :: default ( ) ;
199
+
200
+ while input. peek ( Ident ) {
201
+ let ident: Ident = input. parse ( ) ?;
202
+
203
+ match ident. to_string ( ) . as_str ( ) {
204
+ "value_case" => {
205
+ let _: Token ! [ =] = input. parse ( ) ?;
206
+ let case: LitStr = input. parse ( ) ?;
207
+
208
+ match case. value ( ) . as_str ( ) {
209
+ "lower" => config. value_case = ValueCase :: Lower ,
210
+ "upper" => config. value_case = ValueCase :: Upper ,
211
+ invalid => {
212
+ return Err ( syn:: Error :: new (
213
+ case. span ( ) ,
214
+ format ! (
215
+ "value case may only be \" lower\" or \" upper\" , not \" {invalid}\" "
216
+ ) ,
217
+ ) )
218
+ }
219
+ }
220
+ }
221
+ invalid => {
222
+ return Err ( syn:: Error :: new (
223
+ ident. span ( ) ,
224
+ format ! ( "invalid prometheus attribute \" {invalid}\" " ) ,
225
+ ) )
226
+ }
227
+ }
228
+
229
+ if input. peek ( Token ! [ , ] ) {
230
+ let _: Token ! [ , ] = input. parse ( ) ?;
231
+ }
232
+ }
233
+
234
+ Ok ( config)
235
+ }
236
+ }
237
+
168
238
// Copied from https://github.com/djc/askama (MIT and APACHE licensed) and
169
239
// modified.
170
240
static KEYWORD_IDENTIFIERS : [ ( & str , & str ) ; 48 ] = [
0 commit comments