use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap};
use std::ops::Deref;
use std::pin::Pin;
use parking_lot::{const_rwlock, RwLock, RwLockReadGuard};
use crate::null::NullMetric;
use crate::provide::ProviderMap;
use crate::wrapper::FormattingFn;
use crate::{Format, Metadata, Metric, MetricEntry};
pub(crate) struct DynMetricsRegistry {
metrics: BTreeMap<usize, MetricEntry>,
}
impl DynMetricsRegistry {
const fn new() -> Self {
Self {
metrics: BTreeMap::new(),
}
}
fn key_for(entry: &MetricEntry) -> usize {
entry.metric() as *const dyn Metric as *const () as usize
}
fn register(&mut self, entry: MetricEntry) {
self.metrics.insert(Self::key_for(&entry), entry);
}
fn unregister(&mut self, metric: *const dyn Metric) {
let key = metric as *const () as usize;
self.metrics.remove(&key);
}
pub(crate) fn metrics(&self) -> &BTreeMap<usize, MetricEntry> {
&self.metrics
}
}
static REGISTRY: RwLock<DynMetricsRegistry> = const_rwlock(DynMetricsRegistry::new());
pub(crate) fn get_registry() -> RwLockReadGuard<'static, DynMetricsRegistry> {
REGISTRY.read()
}
pub struct MetricBuilder {
name: Cow<'static, str>,
desc: Option<Cow<'static, str>>,
provider: ProviderMap,
metadata: HashMap<String, String>,
}
impl MetricBuilder {
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
desc: None,
provider: ProviderMap::new(),
metadata: HashMap::new(),
}
}
pub fn description(mut self, desc: impl Into<Cow<'static, str>>) -> Self {
self.desc = Some(desc.into());
self
}
pub fn metadata(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.metadata.insert(key.into(), value.into());
self
}
pub fn formatter(self, formatter: fn(&MetricEntry, Format) -> String) -> Self {
self.provide(FormattingFn(formatter))
}
pub fn provide<T: Send + Sync + 'static>(mut self, value: T) -> Self {
self.provider.insert(value);
self
}
pub fn into_entry(self) -> MetricEntry {
self.build_pinned(NullMetric).1
}
pub fn build<T: Metric>(self, metric: T) -> DynBoxedMetric<T> {
let (metric, entry) = self.build_pinned(metric);
DynBoxedMetric::from_pinned(metric, entry)
}
pub fn build_pinned<T: Metric>(mut self, metric: T) -> (DynPinnedMetric<T>, MetricEntry) {
self.provider.insert(Metadata::new(self.metadata));
let metric = DynPinnedMetric::new_v2(metric, self.provider);
let entry = MetricEntry {
metric: &NullMetric,
name: self.name,
description: self.desc,
module: Cow::Borrowed(""),
};
(metric, entry)
}
}
pub(crate) unsafe fn register(entry: MetricEntry) {
REGISTRY.write().register(entry);
}
pub(crate) fn unregister(metric: *const dyn Metric) {
REGISTRY.write().unregister(metric);
}
struct ProviderMetric<M> {
metric: M,
provider: ProviderMap,
}
impl<M: Metric> Metric for ProviderMetric<M> {
fn is_enabled(&self) -> bool {
self.metric.is_enabled()
}
fn as_any(&self) -> Option<&dyn std::any::Any> {
self.metric.as_any()
}
fn value(&self) -> Option<crate::Value<'_>> {
self.metric.value()
}
fn load_window(&self) -> Option<crate::Window> {
self.metric.load_window()
}
fn value_with_window(&self) -> (Option<crate::Value<'_>>, Option<crate::Window>) {
self.metric.value_with_window()
}
fn provide<'a>(&'a self, request: &mut crate::Request<'a>) {
self.provider.provide(request);
self.metric.provide(request);
}
}
pub struct DynPinnedMetric<M: Metric> {
metric: ProviderMetric<M>,
}
impl<M: Metric> DynPinnedMetric<M> {
#[deprecated = "DynPinnedMetric::new misses some metadata fields. Use MetricBuilder::build_pinned instead."]
pub fn new(metric: M) -> Self {
Self::new_v2(metric, ProviderMap::new())
}
fn new_v2(metric: M, provider: ProviderMap) -> Self {
Self {
metric: ProviderMetric { metric, provider },
}
}
pub fn register(self: Pin<&Self>, mut entry: MetricEntry) {
entry.metric = &self.metric;
unsafe { register(entry) };
}
}
impl<M: Metric> Drop for DynPinnedMetric<M> {
fn drop(&mut self) {
unregister(&self.metric);
}
}
impl<M: Metric> Deref for DynPinnedMetric<M> {
type Target = M;
#[inline]
fn deref(&self) -> &Self::Target {
&self.metric.metric
}
}
pub struct DynBoxedMetric<M: Metric> {
metric: Pin<Box<DynPinnedMetric<M>>>,
}
impl<M: Metric> DynBoxedMetric<M> {
#[deprecated = "DynBoxedMetric::new loses some metadata fields. Use MetricBuilder::build_pinned instead."]
pub fn new(metric: M, entry: MetricEntry) -> Self {
Self::from_pinned(DynPinnedMetric::new_v2(metric, ProviderMap::new()), entry)
}
fn from_pinned(metric: DynPinnedMetric<M>, entry: MetricEntry) -> Self {
let metric = Box::pin(metric);
let this = Self { metric };
this.register(entry);
this
}
fn register(&self, entry: MetricEntry) {
self.metric.as_ref().register(entry)
}
}
impl<M: Metric> Deref for DynBoxedMetric<M> {
type Target = M;
#[inline]
fn deref(&self) -> &Self::Target {
&self.metric
}
}