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}