1use std::str::FromStr;
30
31use crate::obs::v1::{
32 Cardinality, Classification, FieldKind, MetricKind, SamplingReason, Severity, Tier,
33};
34
35#[derive(Debug, thiserror::Error)]
39#[error("unknown {kind} variant: {value:?}")]
40pub struct UnknownVariant {
41 pub kind: &'static str,
43 pub value: String,
45}
46
47#[allow(non_upper_case_globals)]
52impl Tier {
53 pub const Unspecified: Self = Self::TIER_UNSPECIFIED;
55 pub const Log: Self = Self::TIER_LOG;
57 pub const Metric: Self = Self::TIER_METRIC;
59 pub const Trace: Self = Self::TIER_TRACE;
61 pub const Audit: Self = Self::TIER_AUDIT;
63
64 #[must_use]
67 pub const fn as_str(self) -> &'static str {
68 match self {
69 Self::TIER_LOG => "log",
70 Self::TIER_METRIC => "metric",
71 Self::TIER_TRACE => "trace",
72 Self::TIER_AUDIT => "audit",
73 _ => "unspecified",
74 }
75 }
76}
77
78impl Ord for Tier {
79 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
80 (*self as i32).cmp(&(*other as i32))
81 }
82}
83
84impl PartialOrd for Tier {
85 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
86 Some(self.cmp(other))
87 }
88}
89
90impl FromStr for Tier {
91 type Err = UnknownVariant;
92
93 fn from_str(s: &str) -> Result<Self, Self::Err> {
94 match s.to_ascii_lowercase().as_str() {
95 "log" | "tier_log" => Ok(Self::TIER_LOG),
96 "metric" | "tier_metric" => Ok(Self::TIER_METRIC),
97 "trace" | "tier_trace" => Ok(Self::TIER_TRACE),
98 "audit" | "tier_audit" => Ok(Self::TIER_AUDIT),
99 "unspecified" | "tier_unspecified" => Ok(Self::TIER_UNSPECIFIED),
100 _ => Err(UnknownVariant {
101 kind: "Tier",
102 value: s.to_string(),
103 }),
104 }
105 }
106}
107
108impl serde::Serialize for Tier {
109 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
110 s.serialize_str(self.as_str())
111 }
112}
113
114impl<'de> serde::Deserialize<'de> for Tier {
115 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
116 let s = <std::string::String as serde::Deserialize>::deserialize(d)?;
117 Self::from_str(&s).map_err(serde::de::Error::custom)
118 }
119}
120
121#[allow(non_upper_case_globals)]
126impl Severity {
127 pub const Unspecified: Self = Self::SEVERITY_UNSPECIFIED;
129 pub const Trace: Self = Self::SEVERITY_TRACE;
131 pub const Debug: Self = Self::SEVERITY_DEBUG;
133 pub const Info: Self = Self::SEVERITY_INFO;
135 pub const Warn: Self = Self::SEVERITY_WARN;
137 pub const Error: Self = Self::SEVERITY_ERROR;
139 pub const Fatal: Self = Self::SEVERITY_FATAL;
141
142 #[must_use]
144 pub const fn as_str(self) -> &'static str {
145 match self {
146 Self::SEVERITY_TRACE => "trace",
147 Self::SEVERITY_DEBUG => "debug",
148 Self::SEVERITY_INFO => "info",
149 Self::SEVERITY_WARN => "warn",
150 Self::SEVERITY_ERROR => "error",
151 Self::SEVERITY_FATAL => "fatal",
152 _ => "unspecified",
153 }
154 }
155
156 #[must_use]
158 pub const fn otlp_number(self) -> i32 {
159 match self {
160 Self::SEVERITY_TRACE => 1,
161 Self::SEVERITY_DEBUG => 5,
162 Self::SEVERITY_INFO => 9,
163 Self::SEVERITY_WARN => 13,
164 Self::SEVERITY_ERROR => 17,
165 Self::SEVERITY_FATAL => 21,
166 _ => 0,
167 }
168 }
169}
170
171impl Ord for Severity {
172 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
173 (*self as i32).cmp(&(*other as i32))
174 }
175}
176
177impl PartialOrd for Severity {
178 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
179 Some(self.cmp(other))
180 }
181}
182
183impl FromStr for Severity {
184 type Err = UnknownVariant;
185
186 fn from_str(s: &str) -> Result<Self, Self::Err> {
187 match s.to_ascii_lowercase().as_str() {
188 "trace" | "severity_trace" => Ok(Self::SEVERITY_TRACE),
189 "debug" | "severity_debug" => Ok(Self::SEVERITY_DEBUG),
190 "info" | "severity_info" => Ok(Self::SEVERITY_INFO),
191 "warn" | "warning" | "severity_warn" => Ok(Self::SEVERITY_WARN),
192 "error" | "err" | "severity_error" => Ok(Self::SEVERITY_ERROR),
193 "fatal" | "severity_fatal" => Ok(Self::SEVERITY_FATAL),
194 "unspecified" | "severity_unspecified" => Ok(Self::SEVERITY_UNSPECIFIED),
195 _ => Err(UnknownVariant {
196 kind: "Severity",
197 value: s.to_string(),
198 }),
199 }
200 }
201}
202
203impl serde::Serialize for Severity {
204 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
205 s.serialize_str(self.as_str())
206 }
207}
208
209impl<'de> serde::Deserialize<'de> for Severity {
210 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
211 let s = <std::string::String as serde::Deserialize>::deserialize(d)?;
212 Self::from_str(&s).map_err(serde::de::Error::custom)
213 }
214}
215
216#[allow(non_upper_case_globals)]
221impl FieldKind {
222 pub const Unspecified: Self = Self::FIELD_KIND_UNSPECIFIED;
224 pub const Label: Self = Self::LABEL;
226 pub const Attribute: Self = Self::ATTRIBUTE;
228 pub const Measurement: Self = Self::MEASUREMENT;
230 pub const TraceId: Self = Self::TRACE_ID;
232 pub const SpanId: Self = Self::SPAN_ID;
234 pub const ParentSpanId: Self = Self::PARENT_SPAN_ID;
236 pub const TimestampNs: Self = Self::TIMESTAMP_NS;
238 pub const DurationNs: Self = Self::DURATION_NS;
240 pub const Forensic: Self = Self::FORENSIC;
242
243 #[must_use]
245 pub const fn as_str(self) -> &'static str {
246 match self {
247 Self::LABEL => "label",
248 Self::ATTRIBUTE => "attribute",
249 Self::MEASUREMENT => "measurement",
250 Self::TRACE_ID => "trace_id",
251 Self::SPAN_ID => "span_id",
252 Self::PARENT_SPAN_ID => "parent_span_id",
253 Self::TIMESTAMP_NS => "timestamp_ns",
254 Self::DURATION_NS => "duration_ns",
255 Self::FORENSIC => "forensic",
256 _ => "unspecified",
257 }
258 }
259
260 #[must_use]
263 pub const fn is_envelope_lifted(self) -> bool {
264 matches!(
265 self,
266 Self::TRACE_ID | Self::SPAN_ID | Self::PARENT_SPAN_ID | Self::TIMESTAMP_NS
267 )
268 }
269}
270
271impl Ord for FieldKind {
272 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
273 (*self as i32).cmp(&(*other as i32))
274 }
275}
276
277impl PartialOrd for FieldKind {
278 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
279 Some(self.cmp(other))
280 }
281}
282
283impl FromStr for FieldKind {
284 type Err = UnknownVariant;
285
286 fn from_str(s: &str) -> Result<Self, Self::Err> {
287 match s.to_ascii_lowercase().as_str() {
288 "label" => Ok(Self::LABEL),
289 "attribute" => Ok(Self::ATTRIBUTE),
290 "measurement" => Ok(Self::MEASUREMENT),
291 "trace_id" => Ok(Self::TRACE_ID),
292 "span_id" => Ok(Self::SPAN_ID),
293 "parent_span_id" => Ok(Self::PARENT_SPAN_ID),
294 "timestamp_ns" => Ok(Self::TIMESTAMP_NS),
295 "duration_ns" => Ok(Self::DURATION_NS),
296 "forensic" => Ok(Self::FORENSIC),
297 "unspecified" | "field_kind_unspecified" => Ok(Self::FIELD_KIND_UNSPECIFIED),
298 _ => Err(UnknownVariant {
299 kind: "FieldKind",
300 value: s.to_string(),
301 }),
302 }
303 }
304}
305
306impl serde::Serialize for FieldKind {
307 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
308 s.serialize_str(self.as_str())
309 }
310}
311
312impl<'de> serde::Deserialize<'de> for FieldKind {
313 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
314 let s = <std::string::String as serde::Deserialize>::deserialize(d)?;
315 Self::from_str(&s).map_err(serde::de::Error::custom)
316 }
317}
318
319#[allow(non_upper_case_globals)]
324impl Cardinality {
325 pub const Unspecified: Self = Self::CARDINALITY_UNSPECIFIED;
327 pub const Low: Self = Self::LOW;
329 pub const Medium: Self = Self::MEDIUM;
331 pub const High: Self = Self::HIGH;
333 pub const Unbounded: Self = Self::UNBOUNDED;
335
336 #[must_use]
338 pub const fn as_str(self) -> &'static str {
339 match self {
340 Self::LOW => "low",
341 Self::MEDIUM => "medium",
342 Self::HIGH => "high",
343 Self::UNBOUNDED => "unbounded",
344 _ => "unspecified",
345 }
346 }
347
348 #[must_use]
351 pub const fn cap(self) -> u64 {
352 match self {
353 Self::LOW => 10,
354 Self::MEDIUM => 10_000,
355 Self::HIGH => 1_000_000,
356 Self::UNBOUNDED => u64::MAX,
357 _ => 0,
358 }
359 }
360
361 #[must_use]
364 pub const fn is_label_compatible(self) -> bool {
365 matches!(self, Self::LOW | Self::MEDIUM)
366 }
367
368 #[must_use]
371 pub const fn is_measurement_compatible(self) -> bool {
372 !matches!(self, Self::UNBOUNDED)
373 }
374}
375
376impl Ord for Cardinality {
377 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
378 (*self as i32).cmp(&(*other as i32))
379 }
380}
381
382impl PartialOrd for Cardinality {
383 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
384 Some(self.cmp(other))
385 }
386}
387
388impl FromStr for Cardinality {
389 type Err = UnknownVariant;
390
391 fn from_str(s: &str) -> Result<Self, Self::Err> {
392 match s.to_ascii_lowercase().as_str() {
393 "low" => Ok(Self::LOW),
394 "medium" => Ok(Self::MEDIUM),
395 "high" => Ok(Self::HIGH),
396 "unbounded" => Ok(Self::UNBOUNDED),
397 "unspecified" | "cardinality_unspecified" => Ok(Self::CARDINALITY_UNSPECIFIED),
398 _ => Err(UnknownVariant {
399 kind: "Cardinality",
400 value: s.to_string(),
401 }),
402 }
403 }
404}
405
406impl serde::Serialize for Cardinality {
407 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
408 s.serialize_str(self.as_str())
409 }
410}
411
412impl<'de> serde::Deserialize<'de> for Cardinality {
413 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
414 let s = <std::string::String as serde::Deserialize>::deserialize(d)?;
415 Self::from_str(&s).map_err(serde::de::Error::custom)
416 }
417}
418
419#[allow(non_upper_case_globals)]
424impl Classification {
425 pub const Unspecified: Self = Self::CLASSIFICATION_UNSPECIFIED;
427 pub const Internal: Self = Self::INTERNAL;
429 pub const Pii: Self = Self::PII;
431 pub const Secret: Self = Self::SECRET;
433
434 #[must_use]
436 pub const fn as_str(self) -> &'static str {
437 match self {
438 Self::INTERNAL => "internal",
439 Self::PII => "pii",
440 Self::SECRET => "secret",
441 _ => "unspecified",
442 }
443 }
444}
445
446impl Ord for Classification {
447 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
448 (*self as i32).cmp(&(*other as i32))
449 }
450}
451
452impl PartialOrd for Classification {
453 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
454 Some(self.cmp(other))
455 }
456}
457
458impl FromStr for Classification {
459 type Err = UnknownVariant;
460
461 fn from_str(s: &str) -> Result<Self, Self::Err> {
462 match s.to_ascii_lowercase().as_str() {
463 "internal" => Ok(Self::INTERNAL),
464 "pii" => Ok(Self::PII),
465 "secret" => Ok(Self::SECRET),
466 "unspecified" | "classification_unspecified" => Ok(Self::CLASSIFICATION_UNSPECIFIED),
467 _ => Err(UnknownVariant {
468 kind: "Classification",
469 value: s.to_string(),
470 }),
471 }
472 }
473}
474
475impl serde::Serialize for Classification {
476 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
477 s.serialize_str(self.as_str())
478 }
479}
480
481impl<'de> serde::Deserialize<'de> for Classification {
482 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
483 let s = <std::string::String as serde::Deserialize>::deserialize(d)?;
484 Self::from_str(&s).map_err(serde::de::Error::custom)
485 }
486}
487
488#[allow(non_upper_case_globals)]
493impl MetricKind {
494 pub const Unspecified: Self = Self::METRIC_KIND_UNSPECIFIED;
496 pub const Counter: Self = Self::METRIC_KIND_COUNTER;
498 pub const Gauge: Self = Self::METRIC_KIND_GAUGE;
500 pub const Histogram: Self = Self::METRIC_KIND_HISTOGRAM;
502
503 #[must_use]
505 pub const fn as_str(self) -> &'static str {
506 match self {
507 Self::METRIC_KIND_COUNTER => "counter",
508 Self::METRIC_KIND_GAUGE => "gauge",
509 Self::METRIC_KIND_HISTOGRAM => "histogram",
510 _ => "unspecified",
511 }
512 }
513}
514
515impl Ord for MetricKind {
516 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
517 (*self as i32).cmp(&(*other as i32))
518 }
519}
520
521impl PartialOrd for MetricKind {
522 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
523 Some(self.cmp(other))
524 }
525}
526
527impl FromStr for MetricKind {
528 type Err = UnknownVariant;
529
530 fn from_str(s: &str) -> Result<Self, Self::Err> {
531 match s.to_ascii_lowercase().as_str() {
532 "counter" | "metric_kind_counter" => Ok(Self::METRIC_KIND_COUNTER),
533 "gauge" | "metric_kind_gauge" => Ok(Self::METRIC_KIND_GAUGE),
534 "histogram" | "metric_kind_histogram" => Ok(Self::METRIC_KIND_HISTOGRAM),
535 "unspecified" | "metric_kind_unspecified" => Ok(Self::METRIC_KIND_UNSPECIFIED),
536 _ => Err(UnknownVariant {
537 kind: "MetricKind",
538 value: s.to_string(),
539 }),
540 }
541 }
542}
543
544impl serde::Serialize for MetricKind {
545 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
546 s.serialize_str(self.as_str())
547 }
548}
549
550impl<'de> serde::Deserialize<'de> for MetricKind {
551 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
552 let s = <std::string::String as serde::Deserialize>::deserialize(d)?;
553 Self::from_str(&s).map_err(serde::de::Error::custom)
554 }
555}
556
557#[allow(non_upper_case_globals)]
562impl SamplingReason {
563 pub const Unspecified: Self = Self::SAMPLING_REASON_UNSPECIFIED;
565 pub const HeadRate: Self = Self::SAMPLING_REASON_HEAD_RATE;
567 pub const TailError: Self = Self::SAMPLING_REASON_TAIL_ERROR;
569 pub const Slow: Self = Self::SAMPLING_REASON_SLOW;
571 pub const Forensic: Self = Self::SAMPLING_REASON_FORENSIC;
573 pub const Audit: Self = Self::SAMPLING_REASON_AUDIT;
575 pub const Runtime: Self = Self::SAMPLING_REASON_RUNTIME;
577 pub const Override: Self = Self::SAMPLING_REASON_OVERRIDE;
579
580 #[must_use]
582 pub const fn as_str(self) -> &'static str {
583 match self {
584 Self::SAMPLING_REASON_HEAD_RATE => "head_rate",
585 Self::SAMPLING_REASON_TAIL_ERROR => "tail_error",
586 Self::SAMPLING_REASON_SLOW => "slow",
587 Self::SAMPLING_REASON_FORENSIC => "forensic",
588 Self::SAMPLING_REASON_AUDIT => "audit",
589 Self::SAMPLING_REASON_RUNTIME => "runtime",
590 Self::SAMPLING_REASON_OVERRIDE => "override",
591 _ => "unspecified",
592 }
593 }
594}
595
596impl Ord for SamplingReason {
597 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
598 (*self as i32).cmp(&(*other as i32))
599 }
600}
601
602impl PartialOrd for SamplingReason {
603 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
604 Some(self.cmp(other))
605 }
606}
607
608impl FromStr for SamplingReason {
609 type Err = UnknownVariant;
610
611 fn from_str(s: &str) -> Result<Self, Self::Err> {
612 match s.to_ascii_lowercase().as_str() {
613 "head_rate" => Ok(Self::SAMPLING_REASON_HEAD_RATE),
614 "tail_error" => Ok(Self::SAMPLING_REASON_TAIL_ERROR),
615 "slow" => Ok(Self::SAMPLING_REASON_SLOW),
616 "forensic" => Ok(Self::SAMPLING_REASON_FORENSIC),
617 "audit" => Ok(Self::SAMPLING_REASON_AUDIT),
618 "runtime" => Ok(Self::SAMPLING_REASON_RUNTIME),
619 "override" => Ok(Self::SAMPLING_REASON_OVERRIDE),
620 "unspecified" | "sampling_reason_unspecified" => Ok(Self::SAMPLING_REASON_UNSPECIFIED),
621 _ => Err(UnknownVariant {
622 kind: "SamplingReason",
623 value: s.to_string(),
624 }),
625 }
626 }
627}
628
629impl serde::Serialize for SamplingReason {
630 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
631 s.serialize_str(self.as_str())
632 }
633}
634
635impl<'de> serde::Deserialize<'de> for SamplingReason {
636 fn deserialize<D: serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
637 let s = <std::string::String as serde::Deserialize>::deserialize(d)?;
638 Self::from_str(&s).map_err(serde::de::Error::custom)
639 }
640}
641
642#[cfg(test)]
643mod tests {
644 use super::*;
645
646 #[test]
647 fn test_severity_short_name_aliases() {
648 assert_eq!(Severity::Info, Severity::SEVERITY_INFO);
649 assert_eq!(Severity::Warn, Severity::SEVERITY_WARN);
650 }
651
652 #[test]
653 fn test_severity_ord_by_discriminant() {
654 assert!(Severity::Info < Severity::Warn);
655 assert!(Severity::Error < Severity::Fatal);
656 }
657
658 #[test]
659 fn test_severity_otlp_number() {
660 assert_eq!(Severity::Info.otlp_number(), 9);
661 assert_eq!(Severity::Fatal.otlp_number(), 21);
662 }
663
664 #[test]
665 fn test_severity_from_str_aliases() {
666 assert_eq!("warning".parse::<Severity>().unwrap(), Severity::Warn);
667 assert_eq!("err".parse::<Severity>().unwrap(), Severity::Error);
668 assert_eq!("INFO".parse::<Severity>().unwrap(), Severity::Info);
669 assert_eq!("SEVERITY_WARN".parse::<Severity>().unwrap(), Severity::Warn,);
670 }
671
672 #[test]
673 fn test_tier_as_str_and_from_str() {
674 assert_eq!(Tier::Log.as_str(), "log");
675 assert_eq!("AUDIT".parse::<Tier>().unwrap(), Tier::Audit);
676 }
677
678 #[test]
679 fn test_cardinality_caps_and_compat() {
680 assert_eq!(Cardinality::Low.cap(), 10);
681 assert_eq!(Cardinality::Medium.cap(), 10_000);
682 assert_eq!(Cardinality::High.cap(), 1_000_000);
683 assert_eq!(Cardinality::Unbounded.cap(), u64::MAX);
684 assert!(Cardinality::Low.is_label_compatible());
685 assert!(!Cardinality::High.is_label_compatible());
686 }
687
688 #[test]
689 fn test_field_kind_envelope_lifted() {
690 assert!(FieldKind::TraceId.is_envelope_lifted());
691 assert!(FieldKind::SpanId.is_envelope_lifted());
692 assert!(!FieldKind::Label.is_envelope_lifted());
693 }
694
695 #[test]
696 fn test_serde_roundtrip_via_short_name() {
697 let json = serde_json::to_string(&Severity::Info).unwrap();
698 assert_eq!(json, "\"info\"");
699 let back: Severity = serde_json::from_str(&json).unwrap();
700 assert_eq!(back, Severity::Info);
701 }
702}