orion_error/core/
metadata.rs1use std::collections::BTreeMap;
2
3#[derive(Debug, Clone, PartialEq, Eq, Default)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5#[cfg_attr(feature = "serde", serde(transparent))]
6pub struct ErrorMetadata {
16 fields: BTreeMap<String, MetadataValue>,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
21#[cfg_attr(feature = "serde", serde(untagged))]
22pub enum MetadataValue {
27 String(String),
28 Bool(bool),
29 I64(i64),
30 U64(u64),
31}
32
33impl ErrorMetadata {
34 pub fn new() -> Self {
36 Self::default()
37 }
38
39 pub fn is_empty(&self) -> bool {
41 self.fields.is_empty()
42 }
43
44 pub fn as_map(&self) -> &BTreeMap<String, MetadataValue> {
46 &self.fields
47 }
48
49 pub fn insert<K, V>(&mut self, key: K, value: V)
55 where
56 K: Into<String>,
57 V: Into<MetadataValue>,
58 {
59 let key = key.into();
60 debug_assert!(!key.is_empty(), "metadata key must not be empty");
61 if key.is_empty() {
62 return;
63 }
64
65 self.fields.insert(key, value.into());
66 }
67
68 pub fn get(&self, key: &str) -> Option<&MetadataValue> {
70 self.fields.get(key)
71 }
72
73 pub fn get_str(&self, key: &str) -> Option<&str> {
76 match self.get(key) {
77 Some(MetadataValue::String(value)) => Some(value.as_str()),
78 _ => None,
79 }
80 }
81
82 pub fn iter(&self) -> impl Iterator<Item = (&String, &MetadataValue)> {
84 self.fields.iter()
85 }
86
87 pub fn merge_missing(&mut self, other: &ErrorMetadata) {
97 for (key, value) in other.iter() {
98 self.fields
99 .entry(key.clone())
100 .or_insert_with(|| value.clone());
101 }
102 }
103}
104
105impl From<String> for MetadataValue {
106 fn from(value: String) -> Self {
107 Self::String(value)
108 }
109}
110
111impl From<&str> for MetadataValue {
112 fn from(value: &str) -> Self {
113 Self::String(value.to_string())
114 }
115}
116
117impl From<bool> for MetadataValue {
118 fn from(value: bool) -> Self {
119 Self::Bool(value)
120 }
121}
122
123impl From<i64> for MetadataValue {
124 fn from(value: i64) -> Self {
125 Self::I64(value)
126 }
127}
128
129impl From<i32> for MetadataValue {
130 fn from(value: i32) -> Self {
131 Self::I64(i64::from(value))
132 }
133}
134
135impl From<u64> for MetadataValue {
136 fn from(value: u64) -> Self {
137 Self::U64(value)
138 }
139}
140
141impl From<u32> for MetadataValue {
142 fn from(value: u32) -> Self {
143 Self::U64(u64::from(value))
144 }
145}
146
147impl From<usize> for MetadataValue {
148 fn from(value: usize) -> Self {
149 Self::U64(value as u64)
150 }
151}
152
153impl std::fmt::Display for MetadataValue {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 match self {
156 Self::String(s) => write!(f, "{s}"),
157 Self::Bool(b) => write!(f, "{b}"),
158 Self::I64(i) => write!(f, "{i}"),
159 Self::U64(u) => write!(f, "{u}"),
160 }
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::{ErrorMetadata, MetadataValue};
167
168 #[test]
169 fn test_metadata_insert_overwrites_duplicate_keys() {
170 let mut metadata = ErrorMetadata::new();
171 metadata.insert("config.kind", "sink_route");
172 metadata.insert("config.kind", "sink_defaults");
173
174 assert_eq!(metadata.get_str("config.kind"), Some("sink_defaults"));
175 }
176
177 #[test]
178 fn test_metadata_merge_missing_keeps_existing_values() {
179 let mut merged = ErrorMetadata::new();
180 merged.insert("config.kind", "sink_defaults");
181
182 let mut outer = ErrorMetadata::new();
183 outer.insert("config.kind", "sink_route");
184 outer.insert("config.group", "infra");
185
186 merged.merge_missing(&outer);
187
188 assert_eq!(merged.get_str("config.kind"), Some("sink_defaults"));
189 assert_eq!(merged.get_str("config.group"), Some("infra"));
190 }
191
192 #[test]
193 fn test_metadata_get_str_only_for_string_values() {
194 let mut metadata = ErrorMetadata::new();
195 metadata.insert("parse.line", 7u32);
196
197 assert_eq!(metadata.get("parse.line"), Some(&MetadataValue::U64(7)));
198 assert_eq!(metadata.get_str("parse.line"), None);
199 }
200
201 #[test]
202 fn test_metadata_value_display() {
203 assert_eq!(MetadataValue::String("hello".into()).to_string(), "hello");
204 assert_eq!(MetadataValue::Bool(true).to_string(), "true");
205 assert_eq!(MetadataValue::I64(-42).to_string(), "-42");
206 assert_eq!(MetadataValue::U64(256).to_string(), "256");
207 }
208}