use parking_lot::Mutex;
use serde::Serialize;
use serde_value::Value;
use std::sync::{Arc, Weak};
use witchcraft_metrics::Gauge;
pub trait Reduce {
type Input;
type Value: Serialize;
fn default(&self) -> Self::Value;
fn map(&self, v: &Self::Input) -> Self::Value;
fn reduce(&self, a: &mut Self::Value, b: Self::Value);
}
pub struct WeakReducingGauge<R>
where
R: Reduce,
{
entries: Mutex<Vec<Weak<R::Input>>>,
reducer: R,
}
impl<R> WeakReducingGauge<R>
where
R: Reduce + 'static + Sync + Send,
R::Input: 'static + Sync + Send,
{
pub fn new(reducer: R) -> Self {
WeakReducingGauge {
entries: Mutex::new(vec![]),
reducer,
}
}
pub fn push(&self, value: &Arc<R::Input>) {
self.entries.lock().push(Arc::downgrade(value));
}
}
impl<R> Gauge for WeakReducingGauge<R>
where
R: Reduce + 'static + Sync + Send,
R::Input: 'static + Sync + Send,
{
fn value(&self) -> Value {
let mut value = None::<R::Value>;
self.entries.lock().retain(|v| match v.upgrade() {
Some(v) => {
let new_value = self.reducer.map(&v);
match &mut value {
Some(value) => self.reducer.reduce(value, new_value),
None => value = Some(new_value),
}
true
}
None => false,
});
serde_value::to_value(value.unwrap_or_else(|| self.reducer.default())).unwrap()
}
}