mirror of https://github.com/fralalonde/dipstick
154 lines
4.2 KiB
Rust
154 lines
4.2 KiB
Rust
use std::collections::VecDeque;
|
|
use std::ops::{Deref, DerefMut};
|
|
|
|
/// A double-ended vec of strings constituting a metric name or a future part thereof.
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd, Default)]
|
|
pub struct NameParts {
|
|
/// Nodes are stored in order or their final appearance in the names
|
|
/// If there is no namespace, the deque is empty.
|
|
nodes: VecDeque<String>,
|
|
}
|
|
|
|
impl NameParts {
|
|
/// Returns true if this instance is equal to or a subset (more specific) of the target instance.
|
|
/// e.g. `a.b.c` is within `a.b`
|
|
/// e.g. `a.d.c` is not within `a.b`
|
|
pub fn is_within(&self, other: &NameParts) -> bool {
|
|
// quick check: if this name has less parts it cannot be equal or more specific
|
|
if self.len() < other.nodes.len() {
|
|
return false;
|
|
}
|
|
for (i, part) in other.nodes.iter().enumerate() {
|
|
if part != &self.nodes[i] {
|
|
return false;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
/// Make a name in this namespace
|
|
pub fn make_name<S: Into<String>>(&self, leaf: S) -> MetricName {
|
|
let mut nodes = self.clone();
|
|
nodes.push_back(leaf.into());
|
|
MetricName { nodes }
|
|
}
|
|
|
|
/// Extract a copy of the last name part
|
|
/// Panics if empty
|
|
pub fn short(&self) -> MetricName {
|
|
self.back().expect("Short metric name").clone().into()
|
|
}
|
|
}
|
|
|
|
/// Turn any string into a StringDeque
|
|
impl<S: Into<String>> From<S> for NameParts {
|
|
fn from(name_part: S) -> Self {
|
|
let name: String = name_part.into();
|
|
// can we do better than asserting? empty names should not exist, ever...
|
|
debug_assert!(!name.is_empty());
|
|
let mut nodes = NameParts::default();
|
|
nodes.push_front(name);
|
|
nodes
|
|
}
|
|
}
|
|
|
|
/// Enable use of VecDeque methods such as len(), push_*, insert()...
|
|
impl Deref for NameParts {
|
|
type Target = VecDeque<String>;
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.nodes
|
|
}
|
|
}
|
|
|
|
/// Enable use of VecDeque methods such as len(), push_*, insert()...
|
|
impl DerefMut for NameParts {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.nodes
|
|
}
|
|
}
|
|
|
|
/// The name of a metric, including the concatenated possible namespaces in which it was defined.
|
|
#[derive(Debug, Clone, Hash, Eq, PartialEq, Ord, PartialOrd)]
|
|
pub struct MetricName {
|
|
nodes: NameParts,
|
|
}
|
|
|
|
impl MetricName {
|
|
/// Prepend to the existing namespace.
|
|
pub fn prepend<S: Into<NameParts>>(mut self, namespace: S) -> Self {
|
|
let parts: NameParts = namespace.into();
|
|
parts
|
|
.iter()
|
|
.rev()
|
|
.for_each(|node| self.nodes.push_front(node.clone()));
|
|
self
|
|
}
|
|
|
|
/// Append to the existing namespace.
|
|
pub fn append<S: Into<NameParts>>(mut self, namespace: S) -> Self {
|
|
let offset = self.nodes.len() - 1;
|
|
let parts: NameParts = namespace.into();
|
|
for (i, part) in parts.iter().enumerate() {
|
|
self.nodes.insert(i + offset, part.clone())
|
|
}
|
|
self
|
|
}
|
|
|
|
/// Combine name parts into a string.
|
|
pub fn join(&self, separator: &str) -> String {
|
|
self.nodes
|
|
.iter()
|
|
.map(|s| &**s)
|
|
.collect::<Vec<&str>>()
|
|
.join(separator)
|
|
}
|
|
}
|
|
|
|
impl<S: Into<String>> From<S> for MetricName {
|
|
fn from(name: S) -> Self {
|
|
MetricName {
|
|
nodes: NameParts::from(name),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Deref for MetricName {
|
|
type Target = NameParts;
|
|
fn deref(&self) -> &Self::Target {
|
|
&self.nodes
|
|
}
|
|
}
|
|
|
|
impl DerefMut for MetricName {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
&mut self.nodes
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod test {
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn string_deque_within_same() {
|
|
let mut sd1: NameParts = "c".into();
|
|
sd1.push_front("b".into());
|
|
|
|
assert_eq!(true, sd1.is_within(&sd1));
|
|
}
|
|
|
|
#[test]
|
|
fn string_deque_within_other() {
|
|
let mut sd1: NameParts = "b".into();
|
|
sd1.push_front("a".into());
|
|
|
|
let mut sd2: NameParts = "c".into();
|
|
sd2.push_front("b".into());
|
|
sd2.push_front("a".into());
|
|
|
|
assert_eq!(true, sd2.is_within(&sd1));
|
|
assert_eq!(false, sd1.is_within(&sd2));
|
|
}
|
|
}
|