torrust_metrics/metric/
name.rs1use derive_more::Display;
2use serde::{Deserialize, Serialize};
3
4use crate::prometheus::PrometheusSerializable;
5
6#[derive(Debug, Display, Clone, Eq, PartialEq, Default, Deserialize, Serialize, Hash, Ord, PartialOrd)]
7pub struct MetricName(String);
8
9impl MetricName {
10 #[must_use]
16 pub fn new(name: &str) -> Self {
17 assert!(!name.is_empty(), "Metric name cannot be empty.");
18 Self(name.to_owned())
19 }
20}
21
22impl PrometheusSerializable for MetricName {
23 fn to_prometheus(&self) -> String {
24 self.0
30 .chars()
31 .enumerate()
32 .map(|(i, c)| {
33 if i == 0 {
34 if c.is_ascii_alphabetic() || c == '_' || c == ':' {
35 c
36 } else {
37 '_'
38 }
39 } else if c.is_ascii_alphanumeric() || c == '_' || c == ':' {
40 c
41 } else {
42 '_'
43 }
44 })
45 .collect()
46 }
47}
48
49#[macro_export]
50macro_rules! metric_name {
51 ("") => {
52 compile_error!("Metric name cannot be empty");
53 };
54 ($name:literal) => {
55 $crate::metric::name::MetricName::new($name)
56 };
57 ($name:ident) => {
58 $crate::metric::name::MetricName::new($name)
59 };
60}
61
62#[cfg(test)]
63mod tests {
64
65 mod serialization_of_metric_name_to_prometheus {
66
67 use crate::metric::name::MetricName;
68 use crate::prometheus::PrometheusSerializable;
69
70 #[test]
71 fn valid_names_in_prometheus() {
72 assert_eq!(metric_name!("valid_name").to_prometheus(), "valid_name");
73 assert_eq!(metric_name!("_leading_underscore").to_prometheus(), "_leading_underscore");
74 assert_eq!(metric_name!(":leading_colon").to_prometheus(), ":leading_colon");
75 assert_eq!(metric_name!("v123").to_prometheus(), "v123"); assert_eq!(metric_name!("V123").to_prometheus(), "V123"); }
78
79 #[test]
80 fn names_that_need_changes_in_prometheus() {
81 assert_eq!(metric_name!("9invalid_start").to_prometheus(), "_invalid_start");
82 assert_eq!(metric_name!("@test").to_prometheus(), "_test");
83 assert_eq!(metric_name!("invalid-char").to_prometheus(), "invalid_char");
84 assert_eq!(metric_name!("spaces are bad").to_prometheus(), "spaces_are_bad");
85 assert_eq!(metric_name!("a!b@c#d$e%f^g&h*i(j)").to_prometheus(), "a_b_c_d_e_f_g_h_i_j_");
86 assert_eq!(metric_name!("my:metric/version").to_prometheus(), "my:metric_version");
87 assert_eq!(metric_name!("!@#$%^&*()").to_prometheus(), "__________");
88 assert_eq!(metric_name!("ñaca©").to_prometheus(), "_aca_");
89 }
90
91 #[test]
92 #[should_panic(expected = "Metric name cannot be empty.")]
93 fn empty_name() {
94 let _name = MetricName::new("");
95 }
96 }
97}