@@ -50,11 +50,33 @@ impl MetricCollection {
5050 ///
5151 /// Returns an error if a metric name already exists in the current collection.
5252 pub fn merge ( & mut self , other : & Self ) -> Result < ( ) , Error > {
53+ self . check_cross_type_collision ( other) ?;
5354 self . counters . merge ( & other. counters ) ?;
5455 self . gauges . merge ( & other. gauges ) ?;
5556 Ok ( ( ) )
5657 }
5758
59+ /// Returns a set of all metric names in this collection.
60+ fn collect_names ( & self ) -> HashSet < MetricName > {
61+ self . counters . names ( ) . chain ( self . gauges . names ( ) ) . cloned ( ) . collect ( )
62+ }
63+
64+ /// Checks for name collisions between this collection and another one.
65+ fn check_cross_type_collision ( & self , other : & Self ) -> Result < ( ) , Error > {
66+ let self_names: HashSet < _ > = self . collect_names ( ) ;
67+ let other_names: HashSet < _ > = other. collect_names ( ) ;
68+
69+ let cross_type_collisions = self_names. intersection ( & other_names) . next ( ) ;
70+
71+ if let Some ( name) = cross_type_collisions {
72+ return Err ( Error :: MetricNameCollisionInMerge {
73+ metric_name : ( * name) . clone ( ) ,
74+ } ) ;
75+ }
76+
77+ Ok ( ( ) )
78+ }
79+
5880 // Counter-specific methods
5981
6082 pub fn describe_counter ( & mut self , name : & MetricName , opt_unit : Option < Unit > , opt_description : Option < MetricDescription > ) {
@@ -774,6 +796,66 @@ mod tests {
774796 assert_eq ! ( prometheus_output, "" ) ;
775797 }
776798
799+ #[ test]
800+ fn it_should_allow_merging_metric_collections ( ) {
801+ let time = DurationSinceUnixEpoch :: from_secs ( 1_743_552_000 ) ;
802+ let label_set: LabelSet = ( label_name ! ( "label_name" ) , LabelValue :: new ( "value" ) ) . into ( ) ;
803+
804+ let mut collection1 = MetricCollection :: default ( ) ;
805+ collection1
806+ . increase_counter ( & metric_name ! ( "test_counter" ) , & label_set, time)
807+ . unwrap ( ) ;
808+
809+ let mut collection2 = MetricCollection :: default ( ) ;
810+ collection2
811+ . set_gauge ( & metric_name ! ( "test_gauge" ) , & label_set, 1.0 , time)
812+ . unwrap ( ) ;
813+
814+ collection1. merge ( & collection2) . unwrap ( ) ;
815+
816+ assert ! ( collection1. contains_counter( & metric_name!( "test_counter" ) ) ) ;
817+ assert ! ( collection1. contains_gauge( & metric_name!( "test_gauge" ) ) ) ;
818+ }
819+
820+ #[ test]
821+ fn it_should_not_allow_merging_metric_collections_with_name_collisions_for_the_same_metric_types ( ) {
822+ let time = DurationSinceUnixEpoch :: from_secs ( 1_743_552_000 ) ;
823+ let label_set: LabelSet = ( label_name ! ( "label_name" ) , LabelValue :: new ( "value" ) ) . into ( ) ;
824+
825+ let mut collection1 = MetricCollection :: default ( ) ;
826+ collection1
827+ . increase_counter ( & metric_name ! ( "test_metric" ) , & label_set, time)
828+ . unwrap ( ) ;
829+
830+ let mut collection2 = MetricCollection :: default ( ) ;
831+ collection2
832+ . increase_counter ( & metric_name ! ( "test_metric" ) , & label_set, time)
833+ . unwrap ( ) ;
834+ let result = collection1. merge ( & collection2) ;
835+
836+ assert ! ( result. is_err( ) ) ;
837+ }
838+
839+ #[ test]
840+ fn it_should_not_allow_merging_metric_collections_with_name_collisions_for_different_metric_types ( ) {
841+ let time = DurationSinceUnixEpoch :: from_secs ( 1_743_552_000 ) ;
842+ let label_set: LabelSet = ( label_name ! ( "label_name" ) , LabelValue :: new ( "value" ) ) . into ( ) ;
843+
844+ let mut collection1 = MetricCollection :: default ( ) ;
845+ collection1
846+ . increase_counter ( & metric_name ! ( "test_metric" ) , & label_set, time)
847+ . unwrap ( ) ;
848+
849+ let mut collection2 = MetricCollection :: default ( ) ;
850+ collection2
851+ . set_gauge ( & metric_name ! ( "test_metric" ) , & label_set, 1.0 , time)
852+ . unwrap ( ) ;
853+
854+ let result = collection1. merge ( & collection2) ;
855+
856+ assert ! ( result. is_err( ) ) ;
857+ }
858+
777859 mod for_counters {
778860
779861 use pretty_assertions:: assert_eq;
0 commit comments