use std::borrow::Cow;
use std::collections::{btree_map, BTreeMap};
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct MetricId {
name: Cow<'static, str>,
tags: Tags,
}
impl MetricId {
pub fn new<T>(name: T) -> MetricId
where
T: Into<Cow<'static, str>>,
{
MetricId {
name: name.into(),
tags: Tags(BTreeMap::new()),
}
}
pub fn with_tag<K, V>(mut self, key: K, value: V) -> MetricId
where
K: Into<Cow<'static, str>>,
V: Into<Cow<'static, str>>,
{
self.tags.0.insert(key.into(), value.into());
self
}
#[inline]
pub fn name(&self) -> &str {
&self.name
}
#[inline]
pub fn tags(&self) -> &Tags {
&self.tags
}
}
impl From<String> for MetricId {
#[inline]
fn from(s: String) -> Self {
MetricId::new(s)
}
}
impl From<&'static str> for MetricId {
#[inline]
fn from(s: &'static str) -> Self {
MetricId::new(s)
}
}
impl From<Cow<'static, str>> for MetricId {
#[inline]
fn from(s: Cow<'static, str>) -> MetricId {
MetricId::new(s)
}
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Tags(BTreeMap<Cow<'static, str>, Cow<'static, str>>);
impl Tags {
#[inline]
pub fn iter(&self) -> TagsIter<'_> {
TagsIter(self.0.iter())
}
}
impl<'a> IntoIterator for &'a Tags {
type Item = (&'a str, &'a str);
type IntoIter = TagsIter<'a>;
#[inline]
fn into_iter(self) -> TagsIter<'a> {
self.iter()
}
}
pub struct TagsIter<'a>(btree_map::Iter<'a, Cow<'static, str>, Cow<'static, str>>);
impl<'a> Iterator for TagsIter<'a> {
type Item = (&'a str, &'a str);
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|(k, v)| (&**k, &**v))
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<'a> ExactSizeIterator for TagsIter<'a> {}
#[cfg(test)]
mod test {
use crate::MetricId;
#[test]
fn basic() {
let id = MetricId::new("foo.bar")
.with_tag("a", "b")
.with_tag("fizz", "buzz")
.with_tag("fizz", "bazz");
assert_eq!(id.name(), "foo.bar");
assert_eq!(
id.tags().iter().collect::<Vec<_>>(),
&[("a", "b"), ("fizz", "bazz")]
);
}
}