1use std::str::FromStr;
4
5use buffa::Enumeration;
6use serde::{Deserialize, Serialize};
7
8use crate::UnknownVariant;
9
10#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(rename_all = "snake_case")]
15#[repr(i32)]
16#[non_exhaustive]
17pub enum FieldKind {
18 #[default]
20 Unspecified = 0,
21 Label = 1,
23 Attribute = 2,
25 Measurement = 3,
27 TraceId = 4,
29 SpanId = 5,
31 ParentSpanId = 6,
33 TimestampNs = 7,
35 DurationNs = 8,
37 Forensic = 9,
39}
40
41impl FieldKind {
42 #[must_use]
44 pub const fn as_str(self) -> &'static str {
45 match self {
46 Self::Unspecified => "unspecified",
47 Self::Label => "label",
48 Self::Attribute => "attribute",
49 Self::Measurement => "measurement",
50 Self::TraceId => "trace_id",
51 Self::SpanId => "span_id",
52 Self::ParentSpanId => "parent_span_id",
53 Self::TimestampNs => "timestamp_ns",
54 Self::DurationNs => "duration_ns",
55 Self::Forensic => "forensic",
56 }
57 }
58
59 #[must_use]
63 pub const fn is_envelope_lifted(self) -> bool {
64 matches!(
65 self,
66 Self::TraceId | Self::SpanId | Self::ParentSpanId | Self::TimestampNs
67 )
68 }
69}
70
71impl Enumeration for FieldKind {
72 fn from_i32(value: i32) -> Option<Self> {
73 match value {
74 0 => Some(Self::Unspecified),
75 1 => Some(Self::Label),
76 2 => Some(Self::Attribute),
77 3 => Some(Self::Measurement),
78 4 => Some(Self::TraceId),
79 5 => Some(Self::SpanId),
80 6 => Some(Self::ParentSpanId),
81 7 => Some(Self::TimestampNs),
82 8 => Some(Self::DurationNs),
83 9 => Some(Self::Forensic),
84 _ => None,
85 }
86 }
87
88 fn to_i32(&self) -> i32 {
89 *self as i32
90 }
91
92 fn proto_name(&self) -> &'static str {
93 match self {
94 Self::Unspecified => "FIELD_KIND_UNSPECIFIED",
95 Self::Label => "LABEL",
96 Self::Attribute => "ATTRIBUTE",
97 Self::Measurement => "MEASUREMENT",
98 Self::TraceId => "TRACE_ID",
99 Self::SpanId => "SPAN_ID",
100 Self::ParentSpanId => "PARENT_SPAN_ID",
101 Self::TimestampNs => "TIMESTAMP_NS",
102 Self::DurationNs => "DURATION_NS",
103 Self::Forensic => "FORENSIC",
104 }
105 }
106
107 fn from_proto_name(name: &str) -> Option<Self> {
108 match name {
109 "FIELD_KIND_UNSPECIFIED" => Some(Self::Unspecified),
110 "LABEL" => Some(Self::Label),
111 "ATTRIBUTE" => Some(Self::Attribute),
112 "MEASUREMENT" => Some(Self::Measurement),
113 "TRACE_ID" => Some(Self::TraceId),
114 "SPAN_ID" => Some(Self::SpanId),
115 "PARENT_SPAN_ID" => Some(Self::ParentSpanId),
116 "TIMESTAMP_NS" => Some(Self::TimestampNs),
117 "DURATION_NS" => Some(Self::DurationNs),
118 "FORENSIC" => Some(Self::Forensic),
119 _ => None,
120 }
121 }
122
123 fn values() -> &'static [Self] {
124 &[
125 Self::Unspecified,
126 Self::Label,
127 Self::Attribute,
128 Self::Measurement,
129 Self::TraceId,
130 Self::SpanId,
131 Self::ParentSpanId,
132 Self::TimestampNs,
133 Self::DurationNs,
134 Self::Forensic,
135 ]
136 }
137}
138
139impl FromStr for FieldKind {
140 type Err = UnknownVariant;
141
142 fn from_str(s: &str) -> Result<Self, Self::Err> {
143 match s.to_ascii_lowercase().as_str() {
144 "label" => Ok(Self::Label),
145 "attribute" => Ok(Self::Attribute),
146 "measurement" => Ok(Self::Measurement),
147 "trace_id" => Ok(Self::TraceId),
148 "span_id" => Ok(Self::SpanId),
149 "parent_span_id" => Ok(Self::ParentSpanId),
150 "timestamp_ns" => Ok(Self::TimestampNs),
151 "duration_ns" => Ok(Self::DurationNs),
152 "forensic" => Ok(Self::Forensic),
153 _ => Err(UnknownVariant {
154 kind: "FieldKind",
155 value: s.to_string(),
156 }),
157 }
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_should_round_trip_via_i32() {
167 for v in FieldKind::values() {
168 assert_eq!(FieldKind::from_i32(v.to_i32()), Some(*v));
169 }
170 }
171
172 #[test]
173 fn test_should_identify_envelope_lifted() {
174 assert!(FieldKind::TraceId.is_envelope_lifted());
175 assert!(FieldKind::SpanId.is_envelope_lifted());
176 assert!(!FieldKind::Label.is_envelope_lifted());
177 assert!(!FieldKind::Measurement.is_envelope_lifted());
178 }
179}