37
37
//! assert_eq!(expected_msg, buffer);
38
38
//! ```
39
39
40
- use crate :: encoding:: { EncodeExemplarValue , EncodeLabelSet } ;
40
+ use crate :: encoding:: { EncodeExemplarValue , EncodeLabelSet , NoLabelSet } ;
41
41
use crate :: metrics:: exemplar:: Exemplar ;
42
42
use crate :: metrics:: MetricType ;
43
43
use crate :: registry:: { Prefix , Registry , Unit } ;
@@ -324,7 +324,7 @@ impl<'a> MetricEncoder<'a> {
324
324
325
325
self . write_suffix ( "total" ) ?;
326
326
327
- self . encode_labels :: < ( ) > ( None ) ?;
327
+ self . encode_labels :: < NoLabelSet > ( None ) ?;
328
328
329
329
v. encode (
330
330
& mut CounterValueEncoder {
@@ -348,7 +348,7 @@ impl<'a> MetricEncoder<'a> {
348
348
) -> Result < ( ) , std:: fmt:: Error > {
349
349
self . write_prefix_name_unit ( ) ?;
350
350
351
- self . encode_labels :: < ( ) > ( None ) ?;
351
+ self . encode_labels :: < NoLabelSet > ( None ) ?;
352
352
353
353
v. encode (
354
354
& mut GaugeValueEncoder {
@@ -404,14 +404,14 @@ impl<'a> MetricEncoder<'a> {
404
404
) -> Result < ( ) , std:: fmt:: Error > {
405
405
self . write_prefix_name_unit ( ) ?;
406
406
self . write_suffix ( "sum" ) ?;
407
- self . encode_labels :: < ( ) > ( None ) ?;
407
+ self . encode_labels :: < NoLabelSet > ( None ) ?;
408
408
self . writer . write_str ( " " ) ?;
409
409
self . writer . write_str ( dtoa:: Buffer :: new ( ) . format ( sum) ) ?;
410
410
self . newline ( ) ?;
411
411
412
412
self . write_prefix_name_unit ( ) ?;
413
413
self . write_suffix ( "count" ) ?;
414
- self . encode_labels :: < ( ) > ( None ) ?;
414
+ self . encode_labels :: < NoLabelSet > ( None ) ?;
415
415
self . writer . write_str ( " " ) ?;
416
416
self . writer . write_str ( itoa:: Buffer :: new ( ) . format ( count) ) ?;
417
417
self . newline ( ) ?;
@@ -512,12 +512,37 @@ impl<'a> MetricEncoder<'a> {
512
512
additional_labels. encode ( LabelSetEncoder :: new ( self . writer ) . into ( ) ) ?;
513
513
}
514
514
515
- if let Some ( labels) = & self . family_labels {
516
- if !self . const_labels . is_empty ( ) || additional_labels. is_some ( ) {
517
- self . writer . write_str ( "," ) ?;
515
+ /// Writer impl which prepends a comma on the first call to write output to the wrapped writer
516
+ struct CommaPrependingWriter < ' a > {
517
+ writer : & ' a mut dyn Write ,
518
+ should_prepend : bool ,
519
+ }
520
+
521
+ impl Write for CommaPrependingWriter < ' _ > {
522
+ fn write_str ( & mut self , s : & str ) -> std:: fmt:: Result {
523
+ if self . should_prepend {
524
+ self . writer . write_char ( ',' ) ?;
525
+ self . should_prepend = false ;
526
+ }
527
+ self . writer . write_str ( s)
518
528
}
529
+ }
519
530
520
- labels. encode ( LabelSetEncoder :: new ( self . writer ) . into ( ) ) ?;
531
+ if let Some ( labels) = self . family_labels {
532
+ // if const labels or additional labels have been written, a comma must be prepended before writing the family labels.
533
+ // However, it could be the case that the family labels are `Some` and yet empty, so the comma should _only_
534
+ // be prepended if one of the `Write` methods are actually called when attempting to write the family labels.
535
+ // Therefore, wrap the writer on `Self` with a CommaPrependingWriter if other labels have been written and
536
+ // there may be a need to prepend an extra comma before writing additional labels.
537
+ if !self . const_labels . is_empty ( ) || additional_labels. is_some ( ) {
538
+ let mut writer = CommaPrependingWriter {
539
+ writer : self . writer ,
540
+ should_prepend : true ,
541
+ } ;
542
+ labels. encode ( LabelSetEncoder :: new ( & mut writer) . into ( ) ) ?;
543
+ } else {
544
+ labels. encode ( LabelSetEncoder :: new ( self . writer ) . into ( ) ) ?;
545
+ } ;
521
546
}
522
547
523
548
self . writer . write_str ( "}" ) ?;
@@ -709,6 +734,7 @@ mod tests {
709
734
use crate :: metrics:: { counter:: Counter , exemplar:: CounterWithExemplar } ;
710
735
use pyo3:: { prelude:: * , types:: PyModule } ;
711
736
use std:: borrow:: Cow ;
737
+ use std:: fmt:: Error ;
712
738
use std:: sync:: atomic:: { AtomicI32 , AtomicU32 } ;
713
739
714
740
#[ test]
@@ -899,6 +925,31 @@ mod tests {
899
925
parse_with_python_client ( encoded) ;
900
926
}
901
927
928
+ #[ test]
929
+ fn encode_histogram_family_with_empty_struct_family_labels ( ) {
930
+ let mut registry = Registry :: default ( ) ;
931
+ let family =
932
+ Family :: new_with_constructor ( || Histogram :: new ( exponential_buckets ( 1.0 , 2.0 , 10 ) ) ) ;
933
+ registry. register ( "my_histogram" , "My histogram" , family. clone ( ) ) ;
934
+
935
+ #[ derive( Eq , PartialEq , Hash , Debug , Clone ) ]
936
+ struct EmptyLabels { }
937
+
938
+ impl EncodeLabelSet for EmptyLabels {
939
+ fn encode ( & self , _encoder : crate :: encoding:: LabelSetEncoder ) -> Result < ( ) , Error > {
940
+ Ok ( ( ) )
941
+ }
942
+ }
943
+
944
+ family. get_or_create ( & EmptyLabels { } ) . observe ( 1.0 ) ;
945
+
946
+ let mut encoded = String :: new ( ) ;
947
+
948
+ encode ( & mut encoded, & registry) . unwrap ( ) ;
949
+
950
+ parse_with_python_client ( encoded) ;
951
+ }
952
+
902
953
#[ test]
903
954
fn encode_histogram_with_exemplars ( ) {
904
955
let mut registry = Registry :: default ( ) ;
0 commit comments