Skip to main content

obs_types/
metric_kind.rs

1//! [`MetricKind`] — metric type for a `MEASUREMENT` field.
2
3use std::str::FromStr;
4
5use buffa::Enumeration;
6use serde::{Deserialize, Serialize};
7
8use crate::UnknownVariant;
9
10/// Metric type for a `MEASUREMENT` field. See
11/// [12-schema-and-codegen.md §
12/// 2](../../specs/12-schema-and-codegen.md#2-the-obsv1options-proto-extensions).
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15#[repr(i32)]
16#[non_exhaustive]
17pub enum MetricKind {
18    /// Never appears in a well-formed schema.
19    #[default]
20    Unspecified = 0,
21    /// Monotonically increasing counter (e.g. bytes_out, request_count).
22    Counter = 1,
23    /// Last-write-wins gauge (e.g. queue_depth, in_flight).
24    Gauge = 2,
25    /// Histogram with explicit bucket bounds (e.g. latency_ms).
26    Histogram = 3,
27}
28
29impl MetricKind {
30    /// Stable string label.
31    #[must_use]
32    pub const fn as_str(self) -> &'static str {
33        match self {
34            Self::Unspecified => "unspecified",
35            Self::Counter => "counter",
36            Self::Gauge => "gauge",
37            Self::Histogram => "histogram",
38        }
39    }
40}
41
42impl Enumeration for MetricKind {
43    fn from_i32(value: i32) -> Option<Self> {
44        match value {
45            0 => Some(Self::Unspecified),
46            1 => Some(Self::Counter),
47            2 => Some(Self::Gauge),
48            3 => Some(Self::Histogram),
49            _ => None,
50        }
51    }
52
53    fn to_i32(&self) -> i32 {
54        *self as i32
55    }
56
57    fn proto_name(&self) -> &'static str {
58        match self {
59            Self::Unspecified => "METRIC_KIND_UNSPECIFIED",
60            Self::Counter => "METRIC_KIND_COUNTER",
61            Self::Gauge => "METRIC_KIND_GAUGE",
62            Self::Histogram => "METRIC_KIND_HISTOGRAM",
63        }
64    }
65
66    fn from_proto_name(name: &str) -> Option<Self> {
67        match name {
68            "METRIC_KIND_UNSPECIFIED" => Some(Self::Unspecified),
69            "METRIC_KIND_COUNTER" => Some(Self::Counter),
70            "METRIC_KIND_GAUGE" => Some(Self::Gauge),
71            "METRIC_KIND_HISTOGRAM" => Some(Self::Histogram),
72            _ => None,
73        }
74    }
75
76    fn values() -> &'static [Self] {
77        &[
78            Self::Unspecified,
79            Self::Counter,
80            Self::Gauge,
81            Self::Histogram,
82        ]
83    }
84}
85
86impl FromStr for MetricKind {
87    type Err = UnknownVariant;
88
89    fn from_str(s: &str) -> Result<Self, Self::Err> {
90        match s.to_ascii_lowercase().as_str() {
91            "counter" => Ok(Self::Counter),
92            "gauge" => Ok(Self::Gauge),
93            "histogram" => Ok(Self::Histogram),
94            _ => Err(UnknownVariant {
95                kind: "MetricKind",
96                value: s.to_string(),
97            }),
98        }
99    }
100}
101
102#[cfg(test)]
103mod tests {
104    use super::*;
105
106    #[test]
107    fn test_should_round_trip_via_i32() {
108        for v in MetricKind::values() {
109            assert_eq!(MetricKind::from_i32(v.to_i32()), Some(*v));
110        }
111    }
112}