fast_telemetry/metric/
labeled_gauge.rs1use crate::Gauge;
4use crate::label::LabelEnum;
5use std::marker::PhantomData;
6
7pub struct LabeledGauge<L: LabelEnum> {
46 gauges: Vec<Gauge>,
47 _phantom: PhantomData<L>,
48}
49
50impl<L: LabelEnum> LabeledGauge<L> {
51 pub fn new() -> Self {
53 let gauges = (0..L::CARDINALITY).map(|_| Gauge::new()).collect();
54 Self {
55 gauges,
56 _phantom: PhantomData,
57 }
58 }
59
60 #[inline]
62 pub fn set(&self, label: L, value: i64) {
63 let idx = label.as_index();
64 debug_assert!(idx < self.gauges.len(), "label index out of bounds");
65 if cfg!(debug_assertions) {
66 self.gauges[idx].set(value);
67 } else {
68 unsafe { self.gauges.get_unchecked(idx) }.set(value);
69 }
70 }
71
72 #[inline]
74 pub fn get(&self, label: L) -> i64 {
75 let idx = label.as_index();
76 if cfg!(debug_assertions) {
77 self.gauges[idx].get()
78 } else {
79 unsafe { self.gauges.get_unchecked(idx) }.get()
80 }
81 }
82
83 pub fn iter(&self) -> impl Iterator<Item = (L, i64)> + '_ {
87 self.gauges
88 .iter()
89 .enumerate()
90 .map(|(idx, gauge)| (L::from_index(idx), gauge.get()))
91 }
92}
93
94impl<L: LabelEnum> Default for LabeledGauge<L> {
95 fn default() -> Self {
96 Self::new()
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[derive(Copy, Clone, Debug, PartialEq)]
105 enum TestLabel {
106 X,
107 Y,
108 Z,
109 }
110
111 impl LabelEnum for TestLabel {
112 const CARDINALITY: usize = 3;
113 const LABEL_NAME: &'static str = "test";
114
115 fn as_index(self) -> usize {
116 self as usize
117 }
118
119 fn from_index(index: usize) -> Self {
120 match index {
121 0 => Self::X,
122 1 => Self::Y,
123 _ => Self::Z,
124 }
125 }
126
127 fn variant_name(self) -> &'static str {
128 match self {
129 Self::X => "x",
130 Self::Y => "y",
131 Self::Z => "z",
132 }
133 }
134 }
135
136 #[test]
137 fn test_basic_operations() {
138 let gauge: LabeledGauge<TestLabel> = LabeledGauge::new();
139
140 gauge.set(TestLabel::X, 100);
141 gauge.set(TestLabel::Y, 200);
142
143 assert_eq!(gauge.get(TestLabel::X), 100);
144 assert_eq!(gauge.get(TestLabel::Y), 200);
145 assert_eq!(gauge.get(TestLabel::Z), 0);
146 }
147
148 #[test]
149 fn test_overwrite() {
150 let gauge: LabeledGauge<TestLabel> = LabeledGauge::new();
151
152 gauge.set(TestLabel::X, 100);
153 gauge.set(TestLabel::X, 50);
154
155 assert_eq!(gauge.get(TestLabel::X), 50);
156 }
157
158 #[test]
159 fn test_iteration() {
160 let gauge: LabeledGauge<TestLabel> = LabeledGauge::new();
161
162 gauge.set(TestLabel::X, 1);
163 gauge.set(TestLabel::Y, 2);
164 gauge.set(TestLabel::Z, 3);
165
166 let pairs: Vec<_> = gauge.iter().collect();
167 assert_eq!(pairs.len(), 3);
168 assert_eq!(pairs[0], (TestLabel::X, 1));
169 assert_eq!(pairs[1], (TestLabel::Y, 2));
170 assert_eq!(pairs[2], (TestLabel::Z, 3));
171 }
172}