use crate::nonstandard::InfoGauge as InnerInfoGauge;
use parking_lot::{MappedRwLockReadGuard, RwLock, RwLockReadGuard, RwLockWriteGuard};
use prometheus_client::{
encoding::text::{Encode, EncodeMetric, Encoder},
metrics::{family::MetricConstructor, MetricType, TypedMetric},
};
use serde::ser::Serialize;
use std::{collections::HashMap, fmt, hash::Hash, io, sync::Arc};
mod error;
mod str;
mod top;
mod value;
#[derive(Debug)]
pub struct Family<S, M, C = fn() -> M> {
metrics: Arc<RwLock<HashMap<Bridge<S>, M>>>,
constructor: C,
}
impl<S, M, C> Family<S, M, C>
where
S: Clone + Eq + Hash,
{
pub fn new_with_constructor(constructor: C) -> Self {
Self {
metrics: Default::default(),
constructor,
}
}
}
impl<S, M> Default for Family<S, M>
where
S: Clone + Eq + Hash,
M: Default,
{
fn default() -> Self {
Self::new_with_constructor(M::default)
}
}
impl<S, M, C> Family<S, M, C>
where
S: Clone + Eq + Hash,
C: MetricConstructor<M>,
{
pub fn get_or_create(&self, label_set: &S) -> MappedRwLockReadGuard<'_, M> {
let label_set = Bridge::from_ref(label_set);
if let Ok(m) = RwLockReadGuard::try_map(self.metrics.read(), |map| map.get(label_set)) {
return m;
}
let mut map_write = self.metrics.write();
map_write
.entry(label_set.clone())
.or_insert_with(|| self.constructor.new_metric());
let map_read = RwLockWriteGuard::downgrade(map_write);
RwLockReadGuard::map(map_read, |map| {
map.get(label_set)
.expect("metric should exist after creating it")
})
}
pub fn remove(&self, label_set: &S) -> bool {
let label_set = Bridge::from_ref(label_set);
self.metrics.write().remove(label_set).is_some()
}
pub fn clear(&self) {
self.metrics.write().clear();
}
}
impl<S, M, C> EncodeMetric for Family<S, M, C>
where
S: Clone + Eq + Hash + Serialize,
M: EncodeMetric + TypedMetric,
C: MetricConstructor<M>,
{
fn encode(&self, mut encoder: Encoder) -> io::Result<()> {
let map_read = self.metrics.read();
for (label_set, m) in map_read.iter() {
let enc = encoder.with_label_set(label_set);
m.encode(enc)?;
}
Ok(())
}
fn metric_type(&self) -> MetricType {
M::TYPE
}
}
impl<S, M, C> TypedMetric for Family<S, M, C>
where
M: TypedMetric,
{
const TYPE: MetricType = <M as TypedMetric>::TYPE;
}
impl<S, M, C> Clone for Family<S, M, C>
where
C: Clone,
{
fn clone(&self) -> Self {
Self {
metrics: Arc::clone(&self.metrics),
constructor: self.constructor.clone(),
}
}
}
#[derive(Debug)]
pub struct InfoGauge<S> {
inner: InnerInfoGauge<Bridge<S>>,
}
impl<S> InfoGauge<S>
where
S: Serialize,
{
pub fn new(label_set: S) -> Self {
Self {
inner: InnerInfoGauge::new(Bridge(label_set)),
}
}
}
impl<S> EncodeMetric for InfoGauge<S>
where
S: Serialize,
{
fn encode(&self, encoder: Encoder) -> io::Result<()> {
self.inner.encode(encoder)
}
fn metric_type(&self) -> MetricType {
Self::TYPE
}
}
impl<S> TypedMetric for InfoGauge<S>
where
S: Serialize,
{
const TYPE: MetricType = <InnerInfoGauge<S> as TypedMetric>::TYPE;
}
#[derive(Clone, Eq, Hash, PartialEq)]
#[repr(transparent)]
struct Bridge<S>(S);
impl<S> Bridge<S> {
fn from_ref(label_set: &S) -> &Self {
unsafe { &*(label_set as *const S as *const Bridge<S>) }
}
}
impl<S> Encode for Bridge<S>
where
S: Serialize,
{
fn encode(&self, writer: &mut dyn io::Write) -> Result<(), std::io::Error> {
self.0
.serialize(top::serializer(str::Writer::new(writer)))?;
Ok(())
}
}
impl<S> fmt::Debug for Bridge<S>
where
S: fmt::Debug,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}