relay_base_schema/metrics/
name.rs

1use std::fmt;
2use std::sync::Arc;
3
4use serde::{Deserialize, Serialize};
5
6use crate::metrics::{MetricNamespace, MetricType};
7
8/// Optimized string represenation of a metric name.
9///
10/// The contained name does not need to be valid MRI, but it usually is.
11///
12/// The metric name can be efficiently cloned.
13#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
14#[serde(transparent)]
15pub struct MetricName(Arc<str>);
16
17impl MetricName {
18    /// Extracts the type from a well formed MRI.
19    ///
20    /// If the contained metric name is not a well formed MRI this function returns `None`.
21    ///
22    /// # Examples
23    ///
24    /// ```
25    /// use relay_base_schema::metrics::{MetricName, MetricType};
26    ///
27    /// let name = MetricName::from("cfoo");
28    /// assert!(name.try_type().is_none());
29    /// let name = MetricName::from("c:custom/foo@none");
30    /// assert_eq!(name.try_type(), Some(MetricType::Counter));
31    /// let name = MetricName::from("d:custom/foo@none");
32    /// assert_eq!(name.try_type(), Some(MetricType::Distribution));
33    /// let name = MetricName::from("s:custom/foo@none");
34    /// assert_eq!(name.try_type(), Some(MetricType::Set));
35    /// let name = MetricName::from("g:custom/foo@none");
36    /// assert_eq!(name.try_type(), Some(MetricType::Gauge));
37    /// ```
38    pub fn try_type(&self) -> Option<MetricType> {
39        match self.0.as_bytes().get(..2) {
40            Some(b"c:") => Some(MetricType::Counter),
41            Some(b"d:") => Some(MetricType::Distribution),
42            Some(b"s:") => Some(MetricType::Set),
43            Some(b"g:") => Some(MetricType::Gauge),
44            _ => None,
45        }
46    }
47
48    /// Extracts the namespace from a well formed MRI.
49    ///
50    /// Returns [`MetricNamespace::Unsupported`] if the metric name is not a well formed MRI.
51    ///
52    /// # Examples
53    ///
54    /// ```
55    /// use relay_base_schema::metrics::{MetricName, MetricNamespace};
56    ///
57    /// let name = MetricName::from("foo");
58    /// assert_eq!(name.namespace(), MetricNamespace::Unsupported);
59    /// let name = MetricName::from("c:custom_oops/foo@none");
60    /// assert_eq!(name.namespace(), MetricNamespace::Unsupported);
61    ///
62    /// let name = MetricName::from("c:custom/foo@none");
63    /// assert_eq!(name.namespace(), MetricNamespace::Custom);
64    /// ```
65    pub fn namespace(&self) -> MetricNamespace {
66        self.try_namespace().unwrap_or(MetricNamespace::Unsupported)
67    }
68
69    /// Extracts the namespace from a well formed MRI.
70    ///
71    /// If the contained metric name is not a well formed MRI this function returns `None`.
72    ///
73    /// # Examples
74    ///
75    /// ```
76    /// use relay_base_schema::metrics::{MetricName, MetricNamespace};
77    ///
78    /// let name = MetricName::from("foo");
79    /// assert!(name.try_namespace().is_none());
80    /// let name = MetricName::from("c:custom_oops/foo@none");
81    /// assert!(name.try_namespace().is_none());
82    ///
83    /// let name = MetricName::from("c:custom/foo@none");
84    /// assert_eq!(name.try_namespace(), Some(MetricNamespace::Custom));
85    /// ```
86    pub fn try_namespace(&self) -> Option<MetricNamespace> {
87        // A well formed MRI is always in the format `<type>:<namespace>/<name>[@<unit>]`,
88        // `<type>` is always a single ascii character.
89        //
90        // Skip the first two ascii characters and extract the namespace.
91        let maybe_namespace = self.0.get(2..)?.split('/').next()?;
92
93        MetricNamespace::all()
94            .into_iter()
95            .find(|namespace| maybe_namespace == namespace.as_str())
96    }
97}
98
99impl PartialEq<str> for MetricName {
100    fn eq(&self, other: &str) -> bool {
101        self.0.as_ref() == other
102    }
103}
104
105impl fmt::Display for MetricName {
106    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
107        self.0.fmt(f)
108    }
109}
110
111impl From<String> for MetricName {
112    fn from(value: String) -> Self {
113        Self(value.into())
114    }
115}
116
117impl From<Arc<str>> for MetricName {
118    fn from(value: Arc<str>) -> Self {
119        Self(value)
120    }
121}
122
123impl From<&str> for MetricName {
124    fn from(value: &str) -> Self {
125        Self(value.into())
126    }
127}
128
129impl std::ops::Deref for MetricName {
130    type Target = str;
131
132    fn deref(&self) -> &Self::Target {
133        self.0.deref()
134    }
135}
136
137impl AsRef<str> for MetricName {
138    fn as_ref(&self) -> &str {
139        self.0.as_ref()
140    }
141}
142
143impl std::borrow::Borrow<str> for MetricName {
144    fn borrow(&self) -> &str {
145        self.0.borrow()
146    }
147}