1use crate::{Distribution, DynamicHistogramSeriesView, Histogram, exp_buckets::ExpBucketsSnapshot};
4
5#[derive(Clone, Copy, Debug, Eq, PartialEq)]
7pub enum MetricKind {
8 Counter,
9 Gauge,
10 Histogram,
11 Distribution,
12 SampledTimer,
13}
14
15#[derive(Clone, Copy, Debug)]
17pub struct MetricMeta<'a> {
18 pub name: &'a str,
19 pub help: &'a str,
20 pub kind: MetricKind,
21 pub unit: Option<&'a str>,
22}
23
24#[derive(Clone, Copy, Debug, Eq, PartialEq)]
26pub struct MetricLabel<'a> {
27 pub name: &'a str,
28 pub value: &'a str,
29}
30
31#[derive(Clone, Copy, Debug)]
33pub struct MetricLabels<'a> {
34 inner: MetricLabelsInner<'a>,
35}
36
37#[derive(Clone, Copy, Debug)]
38enum MetricLabelsInner<'a> {
39 None,
40 One(MetricLabel<'a>),
41 Slice(&'a [MetricLabel<'a>]),
42 DynamicPairs(&'a [(String, String)]),
43}
44
45#[derive(Debug)]
47pub struct MetricLabelsIter<'a> {
48 inner: MetricLabelsIterInner<'a>,
49}
50
51#[derive(Debug)]
52enum MetricLabelsIterInner<'a> {
53 None,
54 One(Option<MetricLabel<'a>>),
55 Slice(core::slice::Iter<'a, MetricLabel<'a>>),
56 DynamicPairs(core::slice::Iter<'a, (String, String)>),
57}
58
59impl<'a> MetricLabels<'a> {
60 pub const fn none() -> Self {
61 Self {
62 inner: MetricLabelsInner::None,
63 }
64 }
65
66 pub const fn one(label: MetricLabel<'a>) -> Self {
67 Self {
68 inner: MetricLabelsInner::One(label),
69 }
70 }
71
72 pub const fn slice(labels: &'a [MetricLabel<'a>]) -> Self {
73 Self {
74 inner: MetricLabelsInner::Slice(labels),
75 }
76 }
77
78 pub const fn dynamic_pairs(labels: &'a [(String, String)]) -> Self {
79 Self {
80 inner: MetricLabelsInner::DynamicPairs(labels),
81 }
82 }
83
84 pub fn iter(self) -> MetricLabelsIter<'a> {
85 let inner = match self.inner {
86 MetricLabelsInner::None => MetricLabelsIterInner::None,
87 MetricLabelsInner::One(label) => MetricLabelsIterInner::One(Some(label)),
88 MetricLabelsInner::Slice(labels) => MetricLabelsIterInner::Slice(labels.iter()),
89 MetricLabelsInner::DynamicPairs(labels) => {
90 MetricLabelsIterInner::DynamicPairs(labels.iter())
91 }
92 };
93 MetricLabelsIter { inner }
94 }
95}
96
97impl<'a> Iterator for MetricLabelsIter<'a> {
98 type Item = MetricLabel<'a>;
99
100 fn next(&mut self) -> Option<Self::Item> {
101 match &mut self.inner {
102 MetricLabelsIterInner::None => None,
103 MetricLabelsIterInner::One(label) => label.take(),
104 MetricLabelsIterInner::Slice(labels) => labels.next().copied(),
105 MetricLabelsIterInner::DynamicPairs(labels) => {
106 labels.next().map(|(name, value)| MetricLabel {
107 name: name.as_str(),
108 value: value.as_str(),
109 })
110 }
111 }
112 }
113
114 fn size_hint(&self) -> (usize, Option<usize>) {
115 match &self.inner {
116 MetricLabelsIterInner::None => (0, Some(0)),
117 MetricLabelsIterInner::One(Some(_)) => (1, Some(1)),
118 MetricLabelsIterInner::One(None) => (0, Some(0)),
119 MetricLabelsIterInner::Slice(labels) => labels.size_hint(),
120 MetricLabelsIterInner::DynamicPairs(labels) => labels.size_hint(),
121 }
122 }
123}
124
125impl ExactSizeIterator for MetricLabelsIter<'_> {}
126
127pub trait HistogramSnapshot {
129 fn count(&self) -> u64;
130 fn sum(&self) -> u64;
131 fn visit_buckets(&self, visitor: &mut dyn FnMut(u64, u64));
132}
133
134pub trait DistributionSnapshot {
136 fn count(&self) -> u64;
137 fn sum(&self) -> u64;
138 fn min(&self) -> Option<u64>;
139 fn max(&self) -> Option<u64>;
140 fn zero_count(&self) -> u64;
141 fn visit_positive_buckets(&self, visitor: &mut dyn FnMut(i32, u64));
142}
143
144pub trait MetricVisitor {
152 fn counter(&mut self, meta: MetricMeta<'_>, labels: MetricLabels<'_>, value: i64);
153
154 fn gauge_i64(&mut self, meta: MetricMeta<'_>, labels: MetricLabels<'_>, value: i64);
155
156 fn gauge_f64(&mut self, meta: MetricMeta<'_>, labels: MetricLabels<'_>, value: f64);
157
158 fn histogram(
159 &mut self,
160 meta: MetricMeta<'_>,
161 labels: MetricLabels<'_>,
162 histogram: &dyn HistogramSnapshot,
163 );
164
165 fn distribution(
166 &mut self,
167 meta: MetricMeta<'_>,
168 labels: MetricLabels<'_>,
169 distribution: &dyn DistributionSnapshot,
170 ) {
171 let _ = (meta, labels, distribution);
172 }
173
174 fn dynamic_overflow(&mut self, meta: MetricMeta<'_>, overflow_count: u64) {
175 let _ = (meta, overflow_count);
176 }
177}
178
179impl HistogramSnapshot for Histogram {
180 fn count(&self) -> u64 {
181 self.count()
182 }
183
184 fn sum(&self) -> u64 {
185 self.sum()
186 }
187
188 fn visit_buckets(&self, visitor: &mut dyn FnMut(u64, u64)) {
189 for (upper_bound, cumulative_count) in self.buckets_cumulative_iter() {
190 visitor(upper_bound, cumulative_count);
191 }
192 }
193}
194
195impl HistogramSnapshot for DynamicHistogramSeriesView<'_> {
196 fn count(&self) -> u64 {
197 self.count()
198 }
199
200 fn sum(&self) -> u64 {
201 self.sum()
202 }
203
204 fn visit_buckets(&self, visitor: &mut dyn FnMut(u64, u64)) {
205 for (upper_bound, cumulative_count) in self.buckets_cumulative_iter() {
206 visitor(upper_bound, cumulative_count);
207 }
208 }
209}
210
211impl DistributionSnapshot for Distribution {
212 fn count(&self) -> u64 {
213 self.count()
214 }
215
216 fn sum(&self) -> u64 {
217 self.sum()
218 }
219
220 fn min(&self) -> Option<u64> {
221 self.min()
222 }
223
224 fn max(&self) -> Option<u64> {
225 self.max()
226 }
227
228 fn zero_count(&self) -> u64 {
229 self.buckets_snapshot().zero_count
230 }
231
232 fn visit_positive_buckets(&self, visitor: &mut dyn FnMut(i32, u64)) {
233 visit_positive_buckets(&self.buckets_snapshot(), visitor);
234 }
235}
236
237impl DistributionSnapshot for ExpBucketsSnapshot {
238 fn count(&self) -> u64 {
239 self.count
240 }
241
242 fn sum(&self) -> u64 {
243 self.sum
244 }
245
246 fn min(&self) -> Option<u64> {
247 self.min()
248 }
249
250 fn max(&self) -> Option<u64> {
251 self.max()
252 }
253
254 fn zero_count(&self) -> u64 {
255 self.zero_count
256 }
257
258 fn visit_positive_buckets(&self, visitor: &mut dyn FnMut(i32, u64)) {
259 visit_positive_buckets(self, visitor);
260 }
261}
262
263fn visit_positive_buckets(snapshot: &ExpBucketsSnapshot, visitor: &mut dyn FnMut(i32, u64)) {
264 for (index, count) in snapshot.positive.iter().copied().enumerate() {
265 if count > 0 {
266 visitor(index as i32, count);
267 }
268 }
269}
270
271#[cfg(test)]
272mod tests {
273 use super::{MetricLabel, MetricLabels};
274
275 #[test]
276 fn metric_labels_none_iterates_empty() {
277 let labels: Vec<_> = MetricLabels::none().iter().collect();
278 assert!(labels.is_empty());
279 }
280
281 #[test]
282 fn metric_labels_one_iterates_once() {
283 let label = MetricLabel {
284 name: "method",
285 value: "get",
286 };
287
288 let labels: Vec<_> = MetricLabels::one(label).iter().collect();
289 assert_eq!(labels, vec![label]);
290 }
291
292 #[test]
293 fn metric_labels_slice_iterates_borrowed_labels() {
294 let source = [
295 MetricLabel {
296 name: "method",
297 value: "get",
298 },
299 MetricLabel {
300 name: "status",
301 value: "ok",
302 },
303 ];
304
305 let labels: Vec<_> = MetricLabels::slice(&source).iter().collect();
306 assert_eq!(labels, source);
307 }
308
309 #[test]
310 fn metric_labels_dynamic_pairs_iterates_borrowed_strings() {
311 let source = vec![
312 ("endpoint_uuid".to_string(), "ep-1".to_string()),
313 ("org_id".to_string(), "org-a".to_string()),
314 ];
315
316 let labels: Vec<_> = MetricLabels::dynamic_pairs(&source).iter().collect();
317 assert_eq!(
318 labels,
319 vec![
320 MetricLabel {
321 name: "endpoint_uuid",
322 value: "ep-1",
323 },
324 MetricLabel {
325 name: "org_id",
326 value: "org-a",
327 },
328 ]
329 );
330 }
331
332 #[test]
333 fn metric_labels_iterator_reports_exact_len() {
334 let source = vec![
335 ("a".to_string(), "1".to_string()),
336 ("b".to_string(), "2".to_string()),
337 ];
338
339 let mut labels = MetricLabels::dynamic_pairs(&source).iter();
340 assert_eq!(labels.len(), 2);
341 assert_eq!(labels.next().map(|label| label.name), Some("a"));
342 assert_eq!(labels.len(), 1);
343 }
344}