use std::borrow::Cow;
use crate::collector::Collector;
use crate::encoding::{DescriptorEncoder, EncodeMetric};
#[derive(Debug, Default)]
pub struct Registry {
prefix: Option<Prefix>,
labels: Vec<(Cow<'static, str>, Cow<'static, str>)>,
metrics: Vec<(Descriptor, Box<dyn Metric>)>,
collectors: Vec<Box<dyn Collector>>,
sub_registries: Vec<Registry>,
}
impl Registry {
pub fn with_prefix(prefix: impl Into<String>) -> Self {
Self {
prefix: Some(Prefix(prefix.into())),
..Default::default()
}
}
pub fn with_labels(
labels: impl Iterator<Item = (Cow<'static, str>, Cow<'static, str>)>,
) -> Self {
Self {
labels: labels.into_iter().collect(),
..Default::default()
}
}
pub fn with_prefix_and_labels(
prefix: impl Into<String>,
labels: impl Iterator<Item = (Cow<'static, str>, Cow<'static, str>)>,
) -> Self {
Self {
prefix: Some(Prefix(prefix.into())),
labels: labels.into_iter().collect(),
..Default::default()
}
}
pub fn register<N: Into<String>, H: Into<String>>(
&mut self,
name: N,
help: H,
metric: impl Metric,
) {
self.priv_register(name, help, metric, None)
}
pub fn register_with_unit<N: Into<String>, H: Into<String>>(
&mut self,
name: N,
help: H,
unit: Unit,
metric: impl Metric,
) {
self.priv_register(name, help, metric, Some(unit))
}
fn priv_register<N: Into<String>, H: Into<String>>(
&mut self,
name: N,
help: H,
metric: impl Metric,
unit: Option<Unit>,
) {
let descriptor = Descriptor::new(name, help, unit);
self.metrics.push((descriptor, Box::new(metric)));
}
pub fn register_collector(&mut self, collector: Box<dyn Collector>) {
self.collectors.push(collector);
}
pub fn sub_registry_with_prefix<P: AsRef<str>>(&mut self, prefix: P) -> &mut Self {
let sub_registry = Registry {
prefix: Some(Prefix(
self.prefix.clone().map(|p| p.0 + "_").unwrap_or_default() + prefix.as_ref(),
)),
labels: self.labels.clone(),
..Default::default()
};
self.priv_sub_registry(sub_registry)
}
pub fn sub_registry_with_label(
&mut self,
label: (Cow<'static, str>, Cow<'static, str>),
) -> &mut Self {
self.sub_registry_with_labels(std::iter::once(label))
}
pub fn sub_registry_with_labels(
&mut self,
labels: impl Iterator<Item = (Cow<'static, str>, Cow<'static, str>)>,
) -> &mut Self {
let mut new_labels = self.labels.clone();
new_labels.extend(labels);
let sub_registry = Registry {
prefix: self.prefix.clone(),
labels: new_labels,
..Default::default()
};
self.priv_sub_registry(sub_registry)
}
fn priv_sub_registry(&mut self, sub_registry: Self) -> &mut Self {
self.sub_registries.push(sub_registry);
self.sub_registries
.last_mut()
.expect("sub_registries not to be empty.")
}
pub(crate) fn encode(&self, encoder: &mut DescriptorEncoder) -> Result<(), std::fmt::Error> {
for (descriptor, metric) in self.metrics.iter() {
let mut descriptor_encoder =
encoder.with_prefix_and_labels(self.prefix.as_ref(), &self.labels);
let metric_encoder = descriptor_encoder.encode_descriptor(
&descriptor.name,
&descriptor.help,
descriptor.unit.as_ref(),
EncodeMetric::metric_type(metric.as_ref()),
)?;
metric.encode(metric_encoder)?;
}
for collector in self.collectors.iter() {
let descriptor_encoder =
encoder.with_prefix_and_labels(self.prefix.as_ref(), &self.labels);
collector.encode(descriptor_encoder)?;
}
for registry in self.sub_registries.iter() {
registry.encode(encoder)?;
}
Ok(())
}
}
#[derive(Clone, Debug)]
pub(crate) struct Prefix(String);
impl Prefix {
pub(crate) fn as_str(&self) -> &str {
self.0.as_str()
}
}
impl From<String> for Prefix {
fn from(s: String) -> Self {
Prefix(s)
}
}
#[derive(Debug, Clone)]
struct Descriptor {
name: String,
help: String,
unit: Option<Unit>,
}
impl Descriptor {
fn new<N: Into<String>, H: Into<String>>(name: N, help: H, unit: Option<Unit>) -> Self {
Self {
name: name.into(),
help: help.into() + ".",
unit,
}
}
}
#[derive(Debug, Clone)]
#[allow(missing_docs)]
pub enum Unit {
Amperes,
Bytes,
Celsius,
Grams,
Joules,
Meters,
Ratios,
Seconds,
Volts,
Other(String),
}
impl Unit {
pub fn as_str(&self) -> &str {
match self {
Unit::Amperes => "amperes",
Unit::Bytes => "bytes",
Unit::Celsius => "celsius",
Unit::Grams => "grams",
Unit::Joules => "joules",
Unit::Meters => "meters",
Unit::Ratios => "ratios",
Unit::Seconds => "seconds",
Unit::Volts => "volts",
Unit::Other(other) => other.as_str(),
}
}
}
pub trait Metric: crate::encoding::EncodeMetric + Send + Sync + std::fmt::Debug + 'static {}
impl<T> Metric for T where T: crate::encoding::EncodeMetric + Send + Sync + std::fmt::Debug + 'static
{}