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 `{element:?}` contains a single element, two elements (key and value) are required"
71                        )));
72                    }
73                    if element.len() > 2 {
74                        return Err(Error::custom(format!(
75                            "list `{element:?}` contains more than two elements, only two elements (key and value) are allowed"
76                        )));
77                    }
78                    label_list.push((
79                        element.first().unwrap().clone(),
80                        element.last().unwrap().clone(),
81                    ))
82                }
83
84                Ok(Labels::new(&label_list))
85            }
86        }
87        deserializer.deserialize_seq(LabelsVisitor)
88    }
89}
90
91impl Serialize for Labels {
92    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93    where
94        S: Serializer,
95    {
96        let mut seq = serializer.serialize_seq(Some(self.0.len()))?;
97        for label in self.clone().into_labels() {
98            seq.serialize_element(&vec![label.key(), label.value()])?;
99        }
100        seq.end()
101    }
102}
103
104#[cfg(test)]
105mod test {
106    use crate::observability::labels::Labels;
107    use metrics::{IntoLabels, Label};
108
109    #[test]
110    fn test_clone_labels() {
111        let labels = Labels::new(&[("key_1", "value_1")]);
112
113        let labels_2 = labels.clone_with_labels(Labels::new(&[("key_2", "value_2")]));
114        let label_list = labels_2.into_labels();
115        assert!(label_list.contains(&Label::new("key_1", "value_1")));
116        assert!(label_list.contains(&Label::new("key_2", "value_2")));
117
118        let labels_3 =
119            labels.clone_with_labels(Labels::new(&[("key_3", "value_3"), ("key_4", "value_4")]));
120        let label_list = labels_3.into_labels();
121        assert!(label_list.contains(&Label::new("key_1", "value_1")));
122        assert!(!label_list.contains(&Label::new("key_2", "value_2")));
123        assert!(label_list.contains(&Label::new("key_3", "value_3")));
124        assert!(label_list.contains(&Label::new("key_4", "value_4")));
125    }
126
127    use serde_test::{assert_de_tokens_error, assert_tokens, Token};
128
129    #[test]
130    fn test_deserialization_empty() {
131        assert_tokens(
132            &Labels::empty(),
133            &[Token::Seq { len: Some(0) }, Token::SeqEnd],
134        );
135    }
136
137    #[test]
138    fn test_ser_de() {
139        let labels = Labels::new(&[("key_1", "value_1"), ("key_2", "value_2")]);
140
141        assert_tokens(
142            &labels,
143            &[
144                Token::Seq { len: Some(2) },
145                Token::Seq { len: Some(2) },
146                Token::String("key_1"),
147                Token::String("value_1"),
148                Token::SeqEnd,
149                Token::Seq { len: Some(2) },
150                Token::String("key_2"),
151                Token::String("value_2"),
152                Token::SeqEnd,
153                Token::SeqEnd,
154            ],
155        );
156    }
157    #[test]
158    fn test_too_many_elements_for_a_label_should_fail_de() {
159        assert_de_tokens_error::<Labels>(
160            &[
161                Token::Seq { len: Some(1) },
162                Token::Seq { len: Some(3) },
163                Token::String("key_1"),
164                Token::String("value_1"),
165                Token::String("value_2"),
166                Token::SeqEnd,
167                Token::SeqEnd,
168            ],
169            "list `[\"key_1\", \"value_1\", \"value_2\"]` contains more than two elements, only two elements (key and value) are allowed",
170        );
171    }
172    #[test]
173    fn test_single_element_for_a_label_should_fail_de() {
174        assert_de_tokens_error::<Labels>(
175            &[
176                Token::Seq { len: Some(1) },
177                Token::Seq { len: Some(1) },
178                Token::String("key_1"),
179                Token::SeqEnd,
180                Token::SeqEnd,
181            ],
182            "list `[\"key_1\"]` contains a single element, two elements (key and value) are required",
183        );
184    }
185    #[test]
186    fn test_non_string_element_for_a_label_should_fail_de() {
187        assert_de_tokens_error::<Labels>(
188            &[
189                Token::Seq { len: Some(1) },
190                Token::Seq { len: Some(2) },
191                Token::String("key_1"),
192                Token::I8(1),
193                // assert_de_tokens_error requires no remaining token after the failure, so last two tokens should be skipped
194                // Token::SeqEnd,
195                // Token::SeqEnd,
196            ],
197            "invalid type: integer `1`, expected a string",
198        );
199    }
200}