1use core::fmt;
2use std::collections::BTreeMap;
3use std::ops::{AddAssign, SubAssign};
4
5use relay_base_schema::metrics::MetricNamespace;
6
7pub fn tags_cost(tags: &BTreeMap<String, String>) -> usize {
12 tags.iter().map(|(k, v)| k.len() + v.len()).sum()
13}
14
15#[derive(Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct ByNamespace<T> {
18 pub sessions: T,
20 pub transactions: T,
22 pub spans: T,
24 pub custom: T,
26 pub stats: T,
28 pub unsupported: T,
30}
31
32impl<T> ByNamespace<T> {
33 pub fn get(&self, namespace: MetricNamespace) -> &T {
35 match namespace {
36 MetricNamespace::Sessions => &self.sessions,
37 MetricNamespace::Transactions => &self.transactions,
38 MetricNamespace::Spans => &self.spans,
39 MetricNamespace::Custom => &self.custom,
40 MetricNamespace::Stats => &self.stats,
41 MetricNamespace::Unsupported => &self.unsupported,
42 }
43 }
44
45 pub fn get_mut(&mut self, namespace: MetricNamespace) -> &mut T {
47 match namespace {
48 MetricNamespace::Sessions => &mut self.sessions,
49 MetricNamespace::Transactions => &mut self.transactions,
50 MetricNamespace::Spans => &mut self.spans,
51 MetricNamespace::Custom => &mut self.custom,
52 MetricNamespace::Stats => &mut self.stats,
53 MetricNamespace::Unsupported => &mut self.unsupported,
54 }
55 }
56}
57
58impl<T> IntoIterator for ByNamespace<T> {
59 type Item = (MetricNamespace, T);
60 type IntoIter = std::array::IntoIter<(MetricNamespace, T), 6>;
61
62 fn into_iter(self) -> Self::IntoIter {
63 let Self {
64 sessions,
65 transactions,
66 spans,
67 custom,
68 stats,
69 unsupported,
70 } = self;
71
72 [
73 (MetricNamespace::Sessions, sessions),
74 (MetricNamespace::Transactions, transactions),
75 (MetricNamespace::Spans, spans),
76 (MetricNamespace::Custom, custom),
77 (MetricNamespace::Stats, stats),
78 (MetricNamespace::Unsupported, unsupported),
79 ]
80 .into_iter()
81 }
82}
83
84impl<T: fmt::Debug + Default + PartialEq> fmt::Debug for ByNamespace<T> {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 write!(f, "(")?;
88
89 let mut values = MetricNamespace::all()
90 .into_iter()
91 .map(|ns| (ns, self.get(ns)))
92 .filter(|(_, v)| v != &&T::default())
93 .enumerate()
94 .peekable();
95
96 match values.peek() {
97 None => write!(f, "{:?}", T::default())?,
98 Some(_) => {
99 for (i, (namespace, value)) in values {
100 if i > 0 {
101 write!(f, ", ")?;
102 }
103 write!(f, "{namespace}:{value:?}")?;
104 }
105 }
106 }
107
108 write!(f, ")")
109 }
110}
111
112macro_rules! impl_op {
113 ($op:tt, $opfn:ident) => {
114 impl<T: $op> $op for ByNamespace<T> {
115 fn $opfn(&mut self, rhs: Self) {
116 let Self {
117 sessions,
118 transactions,
119 spans,
120 custom,
121 stats,
122 unsupported,
123 } = self;
124
125 $op::$opfn(sessions, rhs.sessions);
126 $op::$opfn(transactions, rhs.transactions);
127 $op::$opfn(spans, rhs.spans);
128 $op::$opfn(custom, rhs.custom);
129 $op::$opfn(stats, rhs.stats);
130 $op::$opfn(unsupported, rhs.unsupported);
131 }
132 }
133 };
134}
135
136impl_op!(AddAssign, add_assign);
137impl_op!(SubAssign, sub_assign);
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_get() {
145 let mut v = ByNamespace::<usize>::default();
146
147 for (i, namespace) in MetricNamespace::all().into_iter().enumerate() {
148 assert_eq!(*v.get(namespace), 0);
149 assert_eq!(*v.get_mut(namespace), 0);
150 *v.get_mut(namespace) += i;
151 assert_eq!(*v.get(namespace), i);
152 }
153
154 for (i, namespace) in MetricNamespace::all().into_iter().enumerate() {
156 assert_eq!(*v.get(namespace), i);
157 assert_eq!(*v.get_mut(namespace), i);
158 }
159 }
160
161 #[test]
162 fn test_add_sub() {
163 let mut v = ByNamespace::<usize>::default();
164 for (i, namespace) in MetricNamespace::all().into_iter().enumerate() {
165 *v.get_mut(namespace) += i;
166 }
167
168 let mut v2 = v;
169 v2 -= v;
170 assert_eq!(v2, Default::default());
171
172 v2 += v;
173 assert_eq!(v2, v);
174 }
175}