Skip to main content

obs_types/
tier.rs

1//! [`Tier`] โ€” primary durable destination for an event.
2
3use std::str::FromStr;
4
5use buffa::Enumeration;
6use serde::{Deserialize, Serialize};
7
8use crate::UnknownVariant;
9
10/// Primary durable destination for an event.
11///
12/// Tier is a routing hint โ€” the same envelope may also fan out to
13/// metric/trace sinks regardless of tier. `Audit` has stricter delivery
14/// semantics (bounded blocking + spool); see [11-runtime-core.md ยง 6.4](
15/// ../../specs/11-runtime-core.md#64-audit-tier-delivery-policy).
16#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
17#[serde(rename_all = "snake_case")]
18#[repr(i32)]
19#[non_exhaustive]
20pub enum Tier {
21    /// `TIER_UNSPECIFIED`; never appears in a well-formed envelope.
22    #[default]
23    Unspecified = 0,
24    /// Durable, queryable; default for most events.
25    Log = 1,
26    /// Aggregated; payload may be discarded after counter increment.
27    Metric = 2,
28    /// Spans; envelope `trace_id` / `span_id` are required.
29    Trace = 3,
30    /// Compliance: separate retention, encryption, immutability.
31    Audit = 4,
32}
33
34impl Tier {
35    /// Stable string label; used by sinks (`labels[\"tier\"]`) and by the
36    /// CLI when rendering. Avoid changing โ€” appears in dashboards.
37    #[must_use]
38    pub const fn as_str(self) -> &'static str {
39        match self {
40            Self::Unspecified => "unspecified",
41            Self::Log => "log",
42            Self::Metric => "metric",
43            Self::Trace => "trace",
44            Self::Audit => "audit",
45        }
46    }
47}
48
49impl Enumeration for Tier {
50    fn from_i32(value: i32) -> Option<Self> {
51        match value {
52            0 => Some(Self::Unspecified),
53            1 => Some(Self::Log),
54            2 => Some(Self::Metric),
55            3 => Some(Self::Trace),
56            4 => Some(Self::Audit),
57            _ => None,
58        }
59    }
60
61    fn to_i32(&self) -> i32 {
62        *self as i32
63    }
64
65    fn proto_name(&self) -> &'static str {
66        match self {
67            Self::Unspecified => "TIER_UNSPECIFIED",
68            Self::Log => "TIER_LOG",
69            Self::Metric => "TIER_METRIC",
70            Self::Trace => "TIER_TRACE",
71            Self::Audit => "TIER_AUDIT",
72        }
73    }
74
75    fn from_proto_name(name: &str) -> Option<Self> {
76        match name {
77            "TIER_UNSPECIFIED" => Some(Self::Unspecified),
78            "TIER_LOG" => Some(Self::Log),
79            "TIER_METRIC" => Some(Self::Metric),
80            "TIER_TRACE" => Some(Self::Trace),
81            "TIER_AUDIT" => Some(Self::Audit),
82            _ => None,
83        }
84    }
85
86    fn values() -> &'static [Self] {
87        &[
88            Self::Unspecified,
89            Self::Log,
90            Self::Metric,
91            Self::Trace,
92            Self::Audit,
93        ]
94    }
95}
96
97impl FromStr for Tier {
98    type Err = UnknownVariant;
99
100    fn from_str(s: &str) -> Result<Self, Self::Err> {
101        match s.to_ascii_lowercase().as_str() {
102            "log" => Ok(Self::Log),
103            "metric" => Ok(Self::Metric),
104            "trace" => Ok(Self::Trace),
105            "audit" => Ok(Self::Audit),
106            _ => Err(UnknownVariant {
107                kind: "Tier",
108                value: s.to_string(),
109            }),
110        }
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn test_should_round_trip_via_i32() {
120        for v in Tier::values() {
121            assert_eq!(Tier::from_i32(v.to_i32()), Some(*v));
122        }
123    }
124
125    #[test]
126    fn test_should_parse_lowercase() {
127        assert_eq!("log".parse::<Tier>().unwrap(), Tier::Log);
128        assert_eq!("AUDIT".parse::<Tier>().unwrap(), Tier::Audit);
129    }
130
131    #[test]
132    fn test_should_reject_unknown() {
133        assert!("garbage".parse::<Tier>().is_err());
134    }
135
136    #[test]
137    fn test_should_round_trip_proto_name() {
138        for v in Tier::values() {
139            assert_eq!(Tier::from_proto_name(v.proto_name()), Some(*v));
140        }
141    }
142}