#![deny(missing_docs)]
use std::{borrow::Cow, fmt, slice::Iter, time::Duration};
pub type ScopedString = Cow<'static, str>;
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Label(ScopedString, ScopedString);
impl Label {
pub fn new<K, V>(key: K, value: V) -> Self
where
K: Into<ScopedString>,
V: Into<ScopedString>,
{
Label(key.into(), value.into())
}
pub fn key(&self) -> &str {
self.0.as_ref()
}
pub fn value(&self) -> &str {
self.1.as_ref()
}
pub fn into_parts(self) -> (ScopedString, ScopedString) {
(self.0, self.1)
}
}
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Key {
name: ScopedString,
labels: Vec<Label>,
}
impl Key {
pub fn from_name<N>(name: N) -> Self
where
N: Into<ScopedString>,
{
Key {
name: name.into(),
labels: Vec::new(),
}
}
pub fn from_name_and_labels<N, L>(name: N, labels: L) -> Self
where
N: Into<ScopedString>,
L: IntoLabels,
{
Key {
name: name.into(),
labels: labels.into_labels(),
}
}
pub fn add_labels<L>(&mut self, new_labels: L)
where
L: IntoLabels,
{
self.labels.extend(new_labels.into_labels());
}
pub fn name(&self) -> ScopedString {
self.name.clone()
}
pub fn labels(&self) -> Iter<Label> {
self.labels.iter()
}
pub fn map_name<F, S>(self, f: F) -> Self
where
F: FnOnce(ScopedString) -> S,
S: Into<ScopedString>,
{
Key {
name: f(self.name).into(),
labels: self.labels,
}
}
pub fn into_parts(self) -> (ScopedString, Vec<Label>) {
(self.name, self.labels)
}
}
impl fmt::Display for Key {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.labels.is_empty() {
write!(f, "Key({})", self.name)
} else {
let kv_pairs = self
.labels
.iter()
.map(|label| format!("{} = {}", label.0, label.1))
.collect::<Vec<_>>();
write!(f, "Key({}, [{}])", self.name, kv_pairs.join(", "))
}
}
}
impl From<String> for Key {
fn from(name: String) -> Key {
Key::from_name(name)
}
}
impl From<&'static str> for Key {
fn from(name: &'static str) -> Key {
Key::from_name(name)
}
}
impl From<ScopedString> for Key {
fn from(name: ScopedString) -> Key {
Key::from_name(name)
}
}
impl<K, L> From<(K, L)> for Key
where
K: Into<ScopedString>,
L: IntoLabels,
{
fn from(parts: (K, L)) -> Key {
Key::from_name_and_labels(parts.0, parts.1)
}
}
impl<K, V> From<(K, V)> for Label
where
K: Into<ScopedString>,
V: Into<ScopedString>,
{
fn from(pair: (K, V)) -> Label {
Label::new(pair.0, pair.1)
}
}
impl<K, V> From<&(K, V)> for Label
where
K: Into<ScopedString> + Clone,
V: Into<ScopedString> + Clone,
{
fn from(pair: &(K, V)) -> Label {
Label::new(pair.0.clone(), pair.1.clone())
}
}
pub trait IntoLabels {
fn into_labels(self) -> Vec<Label>;
}
impl IntoLabels for Vec<Label> {
fn into_labels(self) -> Vec<Label> {
self
}
}
impl<T, L> IntoLabels for &T
where
Self: IntoIterator<Item = L>,
L: Into<Label>,
{
fn into_labels(self) -> Vec<Label> {
self.into_iter().map(|l| l.into()).collect()
}
}
pub trait AsNanoseconds {
fn as_nanos(&self) -> u64;
}
impl AsNanoseconds for u64 {
fn as_nanos(&self) -> u64 {
*self
}
}
impl AsNanoseconds for Duration {
fn as_nanos(&self) -> u64 {
self.as_nanos() as u64
}
}
pub trait Observer {
fn observe_counter(&mut self, key: Key, value: u64);
fn observe_gauge(&mut self, key: Key, value: i64);
fn observe_histogram(&mut self, key: Key, values: &[u64]);
}
pub trait Builder {
type Output: Observer;
fn build(&self) -> Self::Output;
}
pub trait Drain<T> {
fn drain(&mut self) -> T;
}
pub trait Observe {
fn observe<O: Observer>(&self, observer: &mut O);
}
#[macro_export]
macro_rules! labels {
(@ { $($out:expr),* $(,)* } $(,)*) => {
std::vec![ $($out),* ]
};
(@ { } $k:expr => $v:expr, $($rest:tt)*) => {
$crate::labels!(@ { $crate::Label::new($k, $v) } $($rest)*)
};
(@ { $($out:expr),+ } $k:expr => $v:expr, $($rest:tt)*) => {
$crate::labels!(@ { $($out),+, $crate::Label::new($k, $v) } $($rest)*)
};
($($args:tt)*) => {
$crate::labels!(@ { } $($args)*, )
};
}