dd_sds/observability/
labels.rs

1use metrics::{IntoLabels, Label};
2use std::fmt;
3
4use serde::de::{Deserializer, Error, SeqAccess, Visitor};
5use serde::ser::SerializeSeq;
6use serde::{Deserialize, Serialize, Serializer};
7
8/// Holder of multiple [Label] providing some methods to easily clone and adds new labels in it.
9#[derive(Clone, Debug, PartialEq)]
10pub struct Labels(Vec<Label>);
11
12impl Labels {
13    /// Clone the actual [Labels] with additional key-value labels
14    pub fn clone_with_labels(&self, additional_labels: Labels) -> Labels {
15        let mut tags = self.0.clone();
16        tags.extend(additional_labels.into_labels());
17        Labels(tags)
18    }
19
20    pub fn new<'a, T: 'a>(labels: impl IntoIterator<Item = &'a T>) -> Self
21    where
22        Label: From<&'a T>,
23    {
24        Labels(labels.into_iter().map(Label::from).collect())
25    }
26
27    pub const fn empty() -> Self {
28        Labels(vec![])
29    }
30}
31
32impl Default for Labels {
33    fn default() -> Self {
34        Self::empty()
35    }
36}
37
38impl IntoLabels for Labels {
39    fn into_labels(self) -> Vec<Label> {
40        self.0
41    }
42}
43
44impl<'de> Deserialize<'de> for Labels {
45    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
46    where
47        D: Deserializer<'de>,
48    {
49        struct LabelsVisitor;
50
51        impl<'de> Visitor<'de> for LabelsVisitor {
52            type Value = Labels;
53
54            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
55                formatter.write_str("List of pairs of strings")
56            }
57
58            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
59            where
60                A: SeqAccess<'de>,
61            {
62                let mut label_list: Vec<(String, String)> =
63                    Vec::with_capacity(seq.size_hint().unwrap_or(0));
64
65                // While there are elements remaining in the input, add them
66                // into our list.
67                while let Some(element) = seq.next_element::<Vec<String>>()? {
68                    if element.len() < 2 {
69                        return Err(Error::custom(format!(
70                            "list `{:?}` contains a single element, two elements (key and value) are required",
71                            element
72                        )));
73                    }
74                    if element.len() > 2 {
75                        return Err(Error::custom(format!(
76                            "list `{:?}` contains more than two elements, only two elements (key and value) are allowed",
77                            element
78                        )));
79                    }
80                    label_list.push((
81                        element.first().unwrap().clone(),
82                        element.last().unwrap().clone(),
83                    ))
84                }
85
86                Ok(Labels::new(&label_list))
87            }
88        }
89        deserializer.deserialize_seq(LabelsVisitor)
90    }
91}
92
93impl Serialize for Labels {
94    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
95    where
96        S: Serializer,
97    {
98        let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
99        for label in self.clone().into_labels() {
100            seq.serialize_element(&vec![label.key(), label.value()])?;
101        }
102        seq.end()
103    }
104}
105
106#[cfg(test)]
107mod test {
108    use crate::observability::labels::Labels;
109    use metrics::{IntoLabels, Label};
110
111    #[test]
112    fn test_clone_labels() {
113        let labels = Labels::new(&[("key_1", "value_1")]);
114
115        let labels_2 = labels.clone_with_labels(Labels::new(&[("key_2", "value_2")]));
116        let label_list = labels_2.into_labels();
117        assert!(label_list.contains(&Label::new("key_1", "value_1")));
118        assert!(label_list.contains(&Label::new("key_2", "value_2")));
119
120        let labels_3 =
121            labels.clone_with_labels(Labels::new(&[("key_3", "value_3"), ("key_4", "value_4")]));
122        let label_list = labels_3.into_labels();
123        assert!(label_list.contains(&Label::new("key_1", "value_1")));
124        assert!(!label_list.contains(&Label::new("key_2", "value_2")));
125        assert!(label_list.contains(&Label::new("key_3", "value_3")));
126        assert!(label_list.contains(&Label::new("key_4", "value_4")));
127    }
128
129    use serde_test::{assert_de_tokens_error, assert_tokens, Token};
130
131    #[test]
132    fn test_deserialization_empty() {
133        assert_tokens(
134            &Labels::empty(),
135            &[Token::Seq { len: Some(0) }, Token::SeqEnd],
136        );
137    }
138
139    #[test]
140    fn test_ser_de() {
141        let labels = Labels::new(&[("key_1", "value_1"), ("key_2", "value_2")]);
142
143        assert_tokens(
144            &labels,
145            &[
146                Token::Seq { len: Some(2) },
147                Token::Seq { len: Some(2) },
148                Token::String("key_1"),
149                Token::String("value_1"),
150                Token::SeqEnd,
151                Token::Seq { len: Some(2) },
152                Token::String("key_2"),
153                Token::String("value_2"),
154                Token::SeqEnd,
155                Token::SeqEnd,
156            ],
157        );
158    }
159    #[test]
160    fn test_too_many_elements_for_a_label_should_fail_de() {
161        assert_de_tokens_error::<Labels>(
162            &[
163                Token::Seq { len: Some(1) },
164                Token::Seq { len: Some(3) },
165                Token::String("key_1"),
166                Token::String("value_1"),
167                Token::String("value_2"),
168                Token::SeqEnd,
169                Token::SeqEnd,
170            ],
171            "list `[\"key_1\", \"value_1\", \"value_2\"]` contains more than two elements, only two elements (key and value) are allowed",
172        );
173    }
174    #[test]
175    fn test_single_element_for_a_label_should_fail_de() {
176        assert_de_tokens_error::<Labels>(
177            &[
178                Token::Seq { len: Some(1) },
179                Token::Seq { len: Some(1) },
180                Token::String("key_1"),
181                Token::SeqEnd,
182                Token::SeqEnd,
183            ],
184            "list `[\"key_1\"]` contains a single element, two elements (key and value) are required",
185        );
186    }
187    #[test]
188    fn test_non_string_element_for_a_label_should_fail_de() {
189        assert_de_tokens_error::<Labels>(
190            &[
191                Token::Seq { len: Some(1) },
192                Token::Seq { len: Some(2) },
193                Token::String("key_1"),
194                Token::I8(1),
195                // assert_de_tokens_error requires no remaining token after the failure, so last two tokens should be skipped
196                // Token::SeqEnd,
197                // Token::SeqEnd,
198            ],
199            "invalid type: integer `1`, expected a string",
200        );
201    }
202}