kumo_prometheus/
labels.rs1pub trait MetricLabel {
2 fn label_names() -> &'static [&'static str];
3 fn emit_text_value(&self, target: &mut String, value: &str);
4 fn emit_json_value(&self, target: &mut String, value: &str);
5}
6
7#[macro_export]
10macro_rules! count_tts {
11 () => (0);
12 ($one:tt) => (1);
13 ($($a:tt $b:tt)+) => ($crate::count_tts!($($a)+) << 1);
14 ($odd:tt $($a:tt $b:tt)+) => ($crate::count_tts!($($a)+) << 1 | 1);
15}
16
17#[macro_export]
47macro_rules! label_key {
48 (pub struct $name:ident {
49 $(
50 pub $fieldname:ident: String,
51 )*
52 }
53 ) => {
54 $crate::paste::paste!{
55 #[derive(Clone, Hash, Eq, PartialEq)]
56 pub struct $name {
57 $(
58 pub $fieldname: String,
59 )*
60 }
61
62 pub trait [<$name Trait>] {
63 fn key(&self) -> [<Borrowed $name>]<'_>;
64 }
65
66 impl [<$name Trait>] for $name {
67 fn key(&self) -> [<Borrowed $name>]<'_> {
68 [<Borrowed $name>] {
69 $(
70 $fieldname: self.$fieldname.as_str(),
71 )*
72 }
73 }
74 }
75
76 #[derive(Clone, Copy, Hash, Eq, PartialEq)]
79 pub struct [<Borrowed $name>]<'a> {
80 $(
81 pub $fieldname: &'a str,
82 )*
83 }
84
85 impl<'a> [<Borrowed $name>] <'a> {
86 #[allow(unused)]
87 pub fn to_owned(&self) -> $name {
88 $name {
89 $(
90 $fieldname: self.$fieldname.to_string(),
91 )*
92 }
93 }
94
95 #[allow(unused)]
96 pub fn label_pairs(&self) -> [(&str,&str); $crate::count_tts!($($fieldname)*)] {
97 [
98 $(
99 (stringify!($fieldname), self.$fieldname),
100 )*
101 ]
102 }
103 }
104
105 impl<'a> From<&'a [<Borrowed $name>]<'a>> for $name {
106 fn from(value: &'a [<Borrowed $name>]<'_>) -> $name {
107 value.to_owned()
108 }
109 }
110
111 impl<'a> From<&'a dyn [<$name Trait>]> for $name {
112 fn from(value: &'a (dyn [<$name Trait>] + 'a)) -> $name {
113 value.key().to_owned()
114 }
115 }
116
117 impl<'a> [<$name Trait>] for [<Borrowed $name>] <'a> {
118 fn key(&self) -> [<Borrowed $name>]<'_> {
119 *self
120 }
121 }
122
123 impl<'a> ::std::borrow::Borrow<dyn [<$name Trait>] + 'a> for $name {
124 fn borrow(&self) -> &(dyn [<$name Trait>] + 'a) {
125 self
126 }
127 }
128
129 impl<'a> PartialEq for (dyn [<$name Trait>] + 'a) {
130 fn eq(&self, other: &Self) -> bool {
131 self.key().eq(&other.key())
132 }
133 }
134
135 impl<'a> Eq for (dyn [<$name Trait>] + 'a) {}
136 impl<'a> ::std::hash::Hash for (dyn [<$name Trait>] + 'a) {
137 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
138 self.key().hash(state)
139 }
140 }
141
142 impl $crate::labels::MetricLabel for $name {
143 fn label_names() -> &'static [&'static str] {
144 const LABEL_NAMES: &'static [&'static str] = &[
145 $(
146 stringify!($fieldname),
147 )*
148 ];
149
150 LABEL_NAMES
151 }
152
153 fn emit_text_value(&self, target: &mut String, value: &str) {
154 let key = self.key();
155 let pairs = key.label_pairs();
156 target.push('{');
157 for (i, (key, value)) in pairs.iter().enumerate() {
158 if i > 0 {
159 target.push_str(", ");
160 }
161 target.push_str(key);
162 target.push_str("=\"");
163 target.push_str(value);
164 target.push_str("\"");
165 }
166 target.push_str("} ");
167 target.push_str(value);
168 }
169
170 fn emit_json_value(&self, target: &mut String, value: &str) {
171 let key = self.key();
172 let pairs = key.label_pairs();
173
174 if pairs.len() == 1 {
175 target.push('"');
176 target.push_str(pairs[0].1);
177 target.push_str("\":");
178 target.push_str(value);
179 } else {
180 target.push_str("{");
181 for (key, value) in pairs.iter() {
182 target.push_str("\"");
183 target.push_str(key);
184 target.push_str("\":\"");
185 target.push_str(value);
186 target.push_str("\",");
187 }
188
189 target.push_str("\"@\":");
190 target.push_str(value);
191 target.push_str("}");
192 }
193 }
194 }
195 }
196 };
197}
198
199#[cfg(test)]
200mod test {
201 #[test]
202 fn test_label_macro() {
203 label_key! {
204 pub struct MyLabel {
205 pub myname: String,
206 }
207 }
208
209 assert_eq!(
210 BorrowedMyLabel { myname: "hello" }.label_pairs(),
211 [("myname", "hello")]
212 );
213 }
214
215 #[test]
216 fn test_labels_macro() {
217 label_key! {
218 pub struct MyLabels {
219 pub myname: String,
220 pub second_name: String,
221 }
222 }
223
224 assert_eq!(
225 BorrowedMyLabels {
226 myname: "hello",
227 second_name: "there"
228 }
229 .label_pairs(),
230 [("myname", "hello"), ("second_name", "there")]
231 );
232 }
233}