use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap};
use std::marker::PhantomPinned;
use std::mem::ManuallyDrop;
use std::ops::Deref;
use std::pin::Pin;
use crate::null::NullMetric;
use crate::{default_formatter, Format, Metadata, Metric, MetricEntry, MetricWrapper};
use parking_lot::{const_rwlock, RwLock, RwLockReadGuard};
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>>,
metadata: HashMap<String, String>,
formatter: fn(&MetricEntry, Format) -> String,
}
impl MetricBuilder {
pub fn new(name: impl Into<Cow<'static, str>>) -> Self {
Self {
name: name.into(),
desc: None,
metadata: HashMap::new(),
formatter: default_formatter,
}
}
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(mut self, formatter: fn(&MetricEntry, Format) -> String) -> Self {
self.formatter = formatter;
self
}
pub fn into_entry(self) -> MetricEntry {
MetricEntry {
metric: MetricWrapper(&NullMetric),
name: self.name,
description: self.desc,
metadata: Metadata::new(self.metadata),
formatter: self.formatter,
}
}
pub fn build<T: Metric>(self, metric: T) -> DynBoxedMetric<T> {
DynBoxedMetric::new(metric, self.into_entry())
}
}
pub(crate) unsafe fn register(entry: MetricEntry) {
REGISTRY.write().register(entry);
}
pub(crate) fn unregister(metric: *const dyn Metric) {
REGISTRY.write().unregister(metric);
}
union PinnedMetricStorage<M> {
metric: ManuallyDrop<M>,
_padding: u8,
}
impl<M> PinnedMetricStorage<M> {
fn new(metric: M) -> Self {
Self {
metric: ManuallyDrop::new(metric),
}
}
#[inline]
fn metric(&self) -> &M {
unsafe { &self.metric }
}
}
impl<M> Drop for PinnedMetricStorage<M> {
fn drop(&mut self) {
unsafe { ManuallyDrop::drop(&mut self.metric) }
}
}
pub struct DynPinnedMetric<M: Metric> {
storage: PinnedMetricStorage<M>,
_marker: PhantomPinned,
}
impl<M: Metric> DynPinnedMetric<M> {
pub fn new(metric: M) -> Self {
Self {
storage: PinnedMetricStorage::new(metric),
_marker: PhantomPinned,
}
}
pub fn register(self: Pin<&Self>, mut entry: MetricEntry) {
entry.metric = MetricWrapper(self.storage.metric());
unsafe { register(entry) };
}
}
impl<M: Metric> Drop for DynPinnedMetric<M> {
fn drop(&mut self) {
unregister(self.storage.metric());
}
}
impl<M: Metric> Deref for DynPinnedMetric<M> {
type Target = M;
#[inline]
fn deref(&self) -> &Self::Target {
self.storage.metric()
}
}
pub struct DynBoxedMetric<M: Metric> {
metric: Pin<Box<DynPinnedMetric<M>>>,
}
impl<M: Metric> DynBoxedMetric<M> {
pub fn new(metric: M, entry: MetricEntry) -> Self {
let this = Self::unregistered(metric);
this.register(entry);
this
}
fn unregistered(metric: M) -> Self {
Self {
metric: Box::pin(DynPinnedMetric::new(metric)),
}
}
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
}
}