Skip to main content

orion_error/core/
metadata.rs

1use 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 {
7    fields: BTreeMap<String, MetadataValue>,
8}
9
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12#[cfg_attr(feature = "serde", serde(untagged))]
13pub enum MetadataValue {
14    String(String),
15    Bool(bool),
16    I64(i64),
17    U64(u64),
18}
19
20impl ErrorMetadata {
21    pub fn new() -> Self {
22        Self::default()
23    }
24
25    pub fn is_empty(&self) -> bool {
26        self.fields.is_empty()
27    }
28
29    pub fn as_map(&self) -> &BTreeMap<String, MetadataValue> {
30        &self.fields
31    }
32
33    pub fn insert<K, V>(&mut self, key: K, value: V)
34    where
35        K: Into<String>,
36        V: Into<MetadataValue>,
37    {
38        let key = key.into();
39        debug_assert!(!key.is_empty(), "metadata key must not be empty");
40        if key.is_empty() {
41            return;
42        }
43
44        self.fields.insert(key, value.into());
45    }
46
47    pub fn get(&self, key: &str) -> Option<&MetadataValue> {
48        self.fields.get(key)
49    }
50
51    pub fn get_str(&self, key: &str) -> Option<&str> {
52        match self.get(key) {
53            Some(MetadataValue::String(value)) => Some(value.as_str()),
54            _ => None,
55        }
56    }
57
58    pub fn iter(&self) -> impl Iterator<Item = (&String, &MetadataValue)> {
59        self.fields.iter()
60    }
61
62    pub(crate) fn merge_missing(&mut self, other: &ErrorMetadata) {
63        for (key, value) in other.iter() {
64            self.fields
65                .entry(key.clone())
66                .or_insert_with(|| value.clone());
67        }
68    }
69}
70
71impl From<String> for MetadataValue {
72    fn from(value: String) -> Self {
73        Self::String(value)
74    }
75}
76
77impl From<&str> for MetadataValue {
78    fn from(value: &str) -> Self {
79        Self::String(value.to_string())
80    }
81}
82
83impl From<bool> for MetadataValue {
84    fn from(value: bool) -> Self {
85        Self::Bool(value)
86    }
87}
88
89impl From<i64> for MetadataValue {
90    fn from(value: i64) -> Self {
91        Self::I64(value)
92    }
93}
94
95impl From<i32> for MetadataValue {
96    fn from(value: i32) -> Self {
97        Self::I64(i64::from(value))
98    }
99}
100
101impl From<u64> for MetadataValue {
102    fn from(value: u64) -> Self {
103        Self::U64(value)
104    }
105}
106
107impl From<u32> for MetadataValue {
108    fn from(value: u32) -> Self {
109        Self::U64(u64::from(value))
110    }
111}
112
113impl From<usize> for MetadataValue {
114    fn from(value: usize) -> Self {
115        Self::U64(value as u64)
116    }
117}
118
119#[cfg(test)]
120mod tests {
121    use super::{ErrorMetadata, MetadataValue};
122
123    #[test]
124    fn test_metadata_insert_overwrites_duplicate_keys() {
125        let mut metadata = ErrorMetadata::new();
126        metadata.insert("config.kind", "sink_route");
127        metadata.insert("config.kind", "sink_defaults");
128
129        assert_eq!(metadata.get_str("config.kind"), Some("sink_defaults"));
130    }
131
132    #[test]
133    fn test_metadata_merge_missing_keeps_existing_values() {
134        let mut merged = ErrorMetadata::new();
135        merged.insert("config.kind", "sink_defaults");
136
137        let mut outer = ErrorMetadata::new();
138        outer.insert("config.kind", "sink_route");
139        outer.insert("config.group", "infra");
140
141        merged.merge_missing(&outer);
142
143        assert_eq!(merged.get_str("config.kind"), Some("sink_defaults"));
144        assert_eq!(merged.get_str("config.group"), Some("infra"));
145    }
146
147    #[test]
148    fn test_metadata_get_str_only_for_string_values() {
149        let mut metadata = ErrorMetadata::new();
150        metadata.insert("parse.line", 7u32);
151
152        assert_eq!(metadata.get("parse.line"), Some(&MetadataValue::U64(7)));
153        assert_eq!(metadata.get_str("parse.line"), None);
154    }
155}