@@ -9,6 +9,7 @@ use syn::{
9
9
} ;
10
10
11
11
mod parsing;
12
+ mod validation;
12
13
13
14
#[ derive( FromMeta ) ]
14
15
struct MacroArgs {
@@ -279,6 +280,18 @@ fn metric_init(foundations: &Path, fn_: &ItemFn) -> proc_macro2::TokenStream {
279
280
Span :: call_site ( ) ,
280
281
) ;
281
282
283
+ // Validate histogram buckets at compile time if this is a HistogramBuilder
284
+ if let Some ( ctor) = ctor {
285
+ if let Some ( path) = ctor. path . get_ident ( ) {
286
+ if path == "HistogramBuilder" {
287
+ // Validate the histogram buckets
288
+ if let Err ( err) = validation:: validate_histogram_buckets ( ctor) {
289
+ return err. to_compile_error ( ) ;
290
+ }
291
+ }
292
+ }
293
+ }
294
+
282
295
let metric_init = match ctor {
283
296
Some ( ctor) if args. is_empty ( ) => quote ! {
284
297
#reexports:: prometheus_client:: metrics:: family:: MetricConstructor :: new_metric( & ( #ctor) )
@@ -378,7 +391,8 @@ fn metric_fn(foundations: &Path, metrics_struct: &Ident, fn_: &ItemFn) -> proc_m
378
391
mod tests {
379
392
use super :: * ;
380
393
use crate :: common:: test_utils:: { code_str, parse_attr} ;
381
- use syn:: parse_quote;
394
+ use crate :: metrics:: validation;
395
+ use syn:: { parse_quote, ExprStruct } ;
382
396
383
397
#[ test]
384
398
fn expand_empty ( ) {
@@ -736,4 +750,64 @@ mod tests {
736
750
737
751
assert_eq ! ( actual, expected) ;
738
752
}
753
+
754
+ #[ test]
755
+ fn test_valid_histogram_buckets ( ) {
756
+ // Valid strictly increasing buckets
757
+ let expr: ExprStruct = parse_quote ! {
758
+ HistogramBuilder { buckets: & [ 0.1 , 0.2 , 0.3 , 0.4 ] }
759
+ } ;
760
+
761
+ assert ! ( validation:: validate_histogram_buckets( & expr) . is_ok( ) ) ;
762
+ }
763
+
764
+ #[ test]
765
+ fn test_empty_histogram_buckets ( ) {
766
+ // Empty buckets are valid
767
+ let expr: ExprStruct = parse_quote ! {
768
+ HistogramBuilder { buckets: & [ ] }
769
+ } ;
770
+
771
+ assert ! ( validation:: validate_histogram_buckets( & expr) . is_ok( ) ) ;
772
+ }
773
+
774
+ #[ test]
775
+ fn test_invalid_histogram_buckets_equal_values ( ) {
776
+ // Invalid buckets with equal values
777
+ let expr: ExprStruct = parse_quote ! {
778
+ HistogramBuilder { buckets: & [ 0.1 , 0.2 , 0.2 , 0.3 ] }
779
+ } ;
780
+
781
+ assert ! ( validation:: validate_histogram_buckets( & expr) . is_err( ) ) ;
782
+ }
783
+
784
+ #[ test]
785
+ fn test_invalid_histogram_buckets_decreasing_values ( ) {
786
+ // Invalid buckets with decreasing values
787
+ let expr: ExprStruct = parse_quote ! {
788
+ HistogramBuilder { buckets: & [ 0.3 , 0.2 , 0.1 ] }
789
+ } ;
790
+
791
+ assert ! ( validation:: validate_histogram_buckets( & expr) . is_err( ) ) ;
792
+ }
793
+
794
+ #[ test]
795
+ fn test_skip_validation_for_variable_buckets ( ) {
796
+ // Variable buckets should skip validation
797
+ let expr: ExprStruct = parse_quote ! {
798
+ HistogramBuilder { buckets: MY_BUCKETS }
799
+ } ;
800
+
801
+ assert ! ( validation:: validate_histogram_buckets( & expr) . is_ok( ) ) ;
802
+ }
803
+
804
+ #[ test]
805
+ fn test_skip_validation_for_function_buckets ( ) {
806
+ // Function call buckets should skip validation
807
+ let expr: ExprStruct = parse_quote ! {
808
+ HistogramBuilder { buckets: get_buckets( ) }
809
+ } ;
810
+
811
+ assert ! ( validation:: validate_histogram_buckets( & expr) . is_ok( ) ) ;
812
+ }
739
813
}
0 commit comments