metriken_core/
metadata.rs

1use std::collections::HashMap;
2use std::fmt;
3use std::iter::FusedIterator;
4
5/// Metadata for a metric.
6///
7/// Metrics can have arbitrary key-value pairs stored as metadata. This allows
8/// for labelling them with whatever metadata you may find relevant.
9#[derive(Clone)]
10pub struct Metadata(Impl);
11
12#[derive(Clone)]
13enum Impl {
14    Static(&'static phf::Map<&'static str, &'static str>),
15    Dynamic(HashMap<String, String>),
16}
17
18impl Metadata {
19    /// Create a new metadata map from a hashmap.
20    pub fn new(map: HashMap<String, String>) -> Self {
21        Self(Impl::Dynamic(map))
22    }
23
24    pub(crate) const fn default_const() -> Self {
25        const EMPTY_MAP: phf::Map<&str, &str> = phf::Map::new();
26
27        Self::new_static(&EMPTY_MAP)
28    }
29
30    pub(crate) const fn new_static(map: &'static phf::Map<&'static str, &'static str>) -> Self {
31        Self(Impl::Static(map))
32    }
33
34    /// Indicates whether this metadata instance is empty.
35    pub fn is_empty(&self) -> bool {
36        match &self.0 {
37            Impl::Static(map) => map.is_empty(),
38            Impl::Dynamic(map) => map.is_empty(),
39        }
40    }
41
42    /// Get the number of entries contained within this metadata instance.
43    pub fn len(&self) -> usize {
44        match &self.0 {
45            Impl::Static(map) => map.len(),
46            Impl::Dynamic(map) => map.len(),
47        }
48    }
49
50    /// Determins if `key` is in `Metadata`.
51    pub fn contains_key(&self, key: &str) -> bool {
52        match &self.0 {
53            Impl::Static(map) => map.contains_key(key),
54            Impl::Dynamic(map) => map.contains_key(key),
55        }
56    }
57
58    /// Get the value that `key` corresponds to.
59    pub fn get(&self, key: &str) -> Option<&str> {
60        match &self.0 {
61            Impl::Static(map) => map.get(key).copied(),
62            Impl::Dynamic(map) => map.get(key).map(|s| &**s),
63        }
64    }
65
66    /// Return an iterator over the entries of this `Metadata`.
67    pub fn iter(&self) -> MetadataIter {
68        MetadataIter(match &self.0 {
69            Impl::Static(map) => IterImpl::Static(map.entries()),
70            Impl::Dynamic(map) => IterImpl::Dynamic(map.iter()),
71        })
72    }
73}
74
75impl From<HashMap<String, String>> for Metadata {
76    fn from(value: HashMap<String, String>) -> Self {
77        Self(Impl::Dynamic(value))
78    }
79}
80
81impl Default for Metadata {
82    fn default() -> Self {
83        Self::default_const()
84    }
85}
86
87impl fmt::Debug for Metadata {
88    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89        match &self.0 {
90            Impl::Static(map) => map.fmt(f),
91            Impl::Dynamic(map) => map.fmt(f),
92        }
93    }
94}
95
96impl<'a> IntoIterator for &'a Metadata {
97    type Item = (&'a str, &'a str);
98    type IntoIter = MetadataIter<'a>;
99
100    fn into_iter(self) -> Self::IntoIter {
101        self.iter()
102    }
103}
104
105/// An iterator over the entries of a [`Metadata`].
106///
107/// See [`Metadata::iter`].
108#[derive(Clone, Debug)]
109pub struct MetadataIter<'a>(IterImpl<'a>);
110
111impl<'a> MetadataIter<'a> {
112    fn map_entries((key, value): (&&'a str, &&'a str)) -> (&'a str, &'a str) {
113        (*key, *value)
114    }
115
116    fn map_iter((key, value): (&'a String, &'a String)) -> (&'a str, &'a str) {
117        (&**key, &**value)
118    }
119}
120
121#[derive(Clone, Debug)]
122enum IterImpl<'a> {
123    Static(phf::map::Entries<'a, &'static str, &'static str>),
124    Dynamic(std::collections::hash_map::Iter<'a, String, String>),
125}
126
127impl<'a> Iterator for MetadataIter<'a> {
128    type Item = (&'a str, &'a str);
129
130    fn next(&mut self) -> Option<Self::Item> {
131        match &mut self.0 {
132            IterImpl::Static(iter) => iter.next().map(Self::map_entries),
133            IterImpl::Dynamic(iter) => iter.next().map(Self::map_iter),
134        }
135    }
136
137    fn size_hint(&self) -> (usize, Option<usize>) {
138        match &self.0 {
139            IterImpl::Static(iter) => iter.size_hint(),
140            IterImpl::Dynamic(iter) => iter.size_hint(),
141        }
142    }
143
144    fn nth(&mut self, n: usize) -> Option<Self::Item> {
145        match &mut self.0 {
146            IterImpl::Static(iter) => iter.nth(n).map(Self::map_entries),
147            IterImpl::Dynamic(iter) => iter.nth(n).map(Self::map_iter),
148        }
149    }
150
151    fn fold<B, F>(self, init: B, mut f: F) -> B
152    where
153        Self: Sized,
154        F: FnMut(B, Self::Item) -> B,
155    {
156        match self.0 {
157            IterImpl::Static(iter) => iter.fold(init, move |acc, (k, v)| f(acc, (*k, *v))),
158            IterImpl::Dynamic(iter) => iter.fold(init, move |acc, (k, v)| f(acc, (&**k, &**v))),
159        }
160    }
161
162    fn count(self) -> usize
163    where
164        Self: Sized,
165    {
166        self.len()
167    }
168}
169
170impl<'a> ExactSizeIterator for MetadataIter<'a> {}
171
172impl<'a> FusedIterator for MetadataIter<'a> {}