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
179pub trait ExportMetrics {
184 fn visit_metrics<V: MetricVisitor + ?Sized>(&self, visitor: &mut V);
185}
186
187impl HistogramSnapshot for Histogram {
188 fn count(&self) -> u64 {
189 self.count()
190 }
191
192 fn sum(&self) -> u64 {
193 self.sum()
194 }
195
196 fn visit_buckets(&self, visitor: &mut dyn FnMut(u64, u64)) {
197 for (upper_bound, cumulative_count) in self.buckets_cumulative_iter() {
198 visitor(upper_bound, cumulative_count);
199 }
200 }
201}
202
203impl HistogramSnapshot for DynamicHistogramSeriesView<'_> {
204 fn count(&self) -> u64 {
205 self.count()
206 }
207
208 fn sum(&self) -> u64 {
209 self.sum()
210 }
211
212 fn visit_buckets(&self, visitor: &mut dyn FnMut(u64, u64)) {
213 for (upper_bound, cumulative_count) in self.buckets_cumulative_iter() {
214 visitor(upper_bound, cumulative_count);
215 }
216 }
217}
218
219impl DistributionSnapshot for Distribution {
220 fn count(&self) -> u64 {
221 self.count()
222 }
223
224 fn sum(&self) -> u64 {
225 self.sum()
226 }
227
228 fn min(&self) -> Option<u64> {
229 self.min()
230 }
231
232 fn max(&self) -> Option<u64> {
233 self.max()
234 }
235
236 fn zero_count(&self) -> u64 {
237 self.buckets_snapshot().zero_count
238 }
239
240 fn visit_positive_buckets(&self, visitor: &mut dyn FnMut(i32, u64)) {
241 visit_positive_buckets(&self.buckets_snapshot(), visitor);
242 }
243}
244
245impl DistributionSnapshot for ExpBucketsSnapshot {
246 fn count(&self) -> u64 {
247 self.count
248 }
249
250 fn sum(&self) -> u64 {
251 self.sum
252 }
253
254 fn min(&self) -> Option<u64> {
255 self.min()
256 }
257
258 fn max(&self) -> Option<u64> {
259 self.max()
260 }
261
262 fn zero_count(&self) -> u64 {
263 self.zero_count
264 }
265
266 fn visit_positive_buckets(&self, visitor: &mut dyn FnMut(i32, u64)) {
267 visit_positive_buckets(self, visitor);
268 }
269}
270
271fn visit_positive_buckets(snapshot: &ExpBucketsSnapshot, visitor: &mut dyn FnMut(i32, u64)) {
272 for (index, count) in snapshot.positive.iter().copied().enumerate() {
273 if count > 0 {
274 visitor(index as i32, count);
275 }
276 }
277}
278
279#[cfg(test)]
280mod tests {
281 use super::{MetricLabel, MetricLabels};
282
283 #[test]
284 fn metric_labels_none_iterates_empty() {
285 let labels: Vec<_> = MetricLabels::none().iter().collect();
286 assert!(labels.is_empty());
287 }
288
289 #[test]
290 fn metric_labels_one_iterates_once() {
291 let label = MetricLabel {
292 name: "method",
293 value: "get",
294 };
295
296 let labels: Vec<_> = MetricLabels::one(label).iter().collect();
297 assert_eq!(labels, vec![label]);
298 }
299
300 #[test]
301 fn metric_labels_slice_iterates_borrowed_labels() {
302 let source = [
303 MetricLabel {
304 name: "method",
305 value: "get",
306 },
307 MetricLabel {
308 name: "status",
309 value: "ok",
310 },
311 ];
312
313 let labels: Vec<_> = MetricLabels::slice(&source).iter().collect();
314 assert_eq!(labels, source);
315 }
316
317 #[test]
318 fn metric_labels_dynamic_pairs_iterates_borrowed_strings() {
319 let source = vec![
320 ("endpoint_uuid".to_string(), "ep-1".to_string()),
321 ("org_id".to_string(), "org-a".to_string()),
322 ];
323
324 let labels: Vec<_> = MetricLabels::dynamic_pairs(&source).iter().collect();
325 assert_eq!(
326 labels,
327 vec![
328 MetricLabel {
329 name: "endpoint_uuid",
330 value: "ep-1",
331 },
332 MetricLabel {
333 name: "org_id",
334 value: "org-a",
335 },
336 ]
337 );
338 }
339
340 #[test]
341 fn metric_labels_iterator_reports_exact_len() {
342 let source = vec![
343 ("a".to_string(), "1".to_string()),
344 ("b".to_string(), "2".to_string()),
345 ];
346
347 let mut labels = MetricLabels::dynamic_pairs(&source).iter();
348 assert_eq!(labels.len(), 2);
349 assert_eq!(labels.next().map(|label| label.name), Some("a"));
350 assert_eq!(labels.len(), 1);
351 }
352}