use std::{fmt, hash::Hash, ops, sync::Arc};
use once_cell::sync::Lazy;
use crate::{
descriptors::MetricGroupDescriptor,
encoding::LabelGroups,
registry::{CollectToRegistry, MetricsVisitor, Registry},
traits::EncodeLabelSet,
wrappers::FamilyInner,
LazyItem,
};
pub trait Metrics: 'static + Send + Sync {
const DESCRIPTOR: MetricGroupDescriptor;
#[doc(hidden)] fn visit_metrics(&self, visitor: &mut dyn MetricsVisitor);
}
impl<M: Metrics> Metrics for &'static M {
const DESCRIPTOR: MetricGroupDescriptor = M::DESCRIPTOR;
fn visit_metrics(&self, visitor: &mut dyn MetricsVisitor) {
(**self).visit_metrics(visitor);
}
}
impl<M: Metrics> Metrics for Option<M> {
const DESCRIPTOR: MetricGroupDescriptor = M::DESCRIPTOR;
fn visit_metrics(&self, visitor: &mut dyn MetricsVisitor) {
if let Some(metrics) = self {
metrics.visit_metrics(visitor);
}
}
}
#[derive(Debug)]
pub struct Global<M: Metrics>(pub(crate) Lazy<M>);
impl<M: Metrics + Default> Default for Global<M> {
fn default() -> Self {
Self::new()
}
}
impl<M: Metrics + Default> Global<M> {
pub const fn new() -> Self {
Self(Lazy::new(M::default))
}
}
impl<M: Metrics> ops::Deref for Global<M> {
type Target = M;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<M: Metrics> CollectToRegistry for Global<M> {
fn descriptor(&self) -> &'static MetricGroupDescriptor {
&M::DESCRIPTOR
}
fn collect_to_registry(&'static self, registry: &mut Registry) {
registry.register_global_metrics(&self.0, false);
}
}
pub struct MetricsFamily<S, M: Metrics + Default>(Lazy<FamilyInner<S, M>>);
impl<S, M> fmt::Debug for MetricsFamily<S, M>
where
S: Clone + Eq + Hash + fmt::Debug,
M: Metrics + Default + fmt::Debug,
{
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, formatter)
}
}
impl<S, M> Default for MetricsFamily<S, M>
where
S: Clone + Eq + Hash,
M: Metrics + Default,
{
fn default() -> Self {
Self::new()
}
}
impl<S, M> MetricsFamily<S, M>
where
S: Clone + Eq + Hash,
M: Metrics + Default,
{
pub const fn new() -> Self {
Self(Lazy::new(|| FamilyInner::new(())))
}
pub fn get_lazy(&self, labels: S) -> LazyItem<'_, S, M> {
LazyItem::new(&self.0, labels)
}
pub fn to_entries(&self) -> impl ExactSizeIterator<Item = (S, &M)> + '_ {
self.0.to_entries()
}
}
impl<S, M> ops::Index<&S> for MetricsFamily<S, M>
where
S: Clone + Eq + Hash,
M: Metrics + Default,
{
type Output = M;
fn index(&self, labels: &S) -> &Self::Output {
self.0.get_or_create(labels)
}
}
impl<S, M> Metrics for FamilyInner<S, M>
where
S: EncodeLabelSet + Clone + Eq + Hash + Send + Sync + 'static,
M: Metrics + Default,
{
const DESCRIPTOR: MetricGroupDescriptor = M::DESCRIPTOR;
fn visit_metrics(&self, visitor: &mut dyn MetricsVisitor) {
let mut grouped = LabelGroups::default();
for (labels, metrics) in self.to_entries() {
grouped.set_labels(Arc::new(labels));
metrics.visit_metrics(&mut grouped);
}
grouped.visit_metrics(visitor);
}
}
impl<S, M> Metrics for MetricsFamily<S, M>
where
S: EncodeLabelSet + Clone + Eq + Hash + Send + Sync + 'static,
M: Metrics + Default,
{
const DESCRIPTOR: MetricGroupDescriptor = M::DESCRIPTOR;
fn visit_metrics(&self, visitor: &mut dyn MetricsVisitor) {
if let Some(inner) = Lazy::get(&self.0) {
inner.visit_metrics(visitor);
}
}
}
impl<S, M> CollectToRegistry for MetricsFamily<S, M>
where
S: EncodeLabelSet + Clone + Eq + Hash + Send + Sync + 'static,
M: Metrics + Default,
{
fn descriptor(&self) -> &'static MetricGroupDescriptor {
&M::DESCRIPTOR
}
fn collect_to_registry(&'static self, registry: &mut Registry) {
registry.register_global_metrics(&self.0, true);
}
}