relay_event_normalization/eap/
trace_metric.rs

1//! Normalizations specific to trace metrics.
2
3use std::{borrow::Cow, sync::LazyLock};
4
5use regex::Regex;
6use relay_event_schema::protocol::TraceMetric;
7
8/// Returned by [`normalize_metric_name`].
9#[derive(Debug, thiserror::Error, PartialEq, Eq)]
10#[error("Metric has an invalid name")]
11pub struct InvalidMetricName;
12
13/// Normalizes a trace metric name.
14///
15/// Metric names cannot be empty, must only consist of ASCII alphanumerics, underscores, dashes, and periods.
16/// The implementation will replace dashes with underscores.
17///
18/// Empty metric names are rejected with [`InvalidMetricName`].
19pub fn normalize_metric_name(metric: &mut TraceMetric) -> Result<(), InvalidMetricName> {
20    static NORMALIZE_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new("[^a-zA-Z0-9_.]+").unwrap());
21
22    let name = metric.name.value_mut().as_mut();
23    let Some(name) = name.filter(|s| !s.trim().is_empty()) else {
24        return Err(InvalidMetricName);
25    };
26
27    if let Cow::Owned(new_name) = NORMALIZE_RE.replace_all(name, "_") {
28        *name = new_name;
29    }
30
31    Ok(())
32}
33
34#[cfg(test)]
35mod tests {
36    use relay_protocol::Annotated;
37
38    use super::*;
39
40    fn metric(name: impl Into<String>) -> TraceMetric {
41        TraceMetric {
42            name: Annotated::new(name.into()),
43            ..Default::default()
44        }
45    }
46
47    macro_rules! assert_metric_name {
48        ($name:expr, err) => {{
49            assert_eq!(
50                normalize_metric_name(&mut metric($name)),
51                Err(InvalidMetricName)
52            )
53        }};
54        ($name:expr, $expected:expr) => {{
55            let mut metric = metric($name);
56            assert_eq!(normalize_metric_name(&mut metric), Ok(()));
57            let name = metric.name.value_mut().as_mut().unwrap();
58            assert_eq!(name, $expected);
59        }};
60    }
61
62    #[test]
63    fn test_normalize_name_invalid() {
64        assert_metric_name!("", err);
65        assert_metric_name!("     ", err);
66    }
67
68    #[test]
69    fn test_normalize_metric_name() {
70        assert_metric_name!("foo.bar123", "foo.bar123");
71        assert_metric_name!("foo bar", "foo_bar");
72        assert_metric_name!("foo!@#bar", "foo_bar");
73        assert_metric_name!("   foo.bar    ", "_foo.bar_");
74        assert_metric_name!("unicøøde", "unic_de");
75    }
76}