use std::fmt;
use std::sync::Mutex;
use std::time::{Duration, Instant};
#[cfg(feature = "ser")]
use serde::ser::SerializeMap;
#[cfg(feature = "ser")]
use serde::{Serialize, Serializer};
pub trait GaugeFn: Send + Sync {
fn value(&self) -> f64;
}
impl<F: Fn() -> f64 + Send + Sync> GaugeFn for F {
fn value(&self) -> f64 {
self()
}
}
pub struct Gauge {
func: Box<dyn GaugeFn>,
}
impl Gauge {
pub(crate) fn new(f: Box<dyn GaugeFn>) -> Gauge {
Gauge { func: f }
}
pub fn value(&self) -> f64 {
self.func.value()
}
}
impl fmt::Debug for Gauge {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Gauge").finish()
}
}
#[cfg(feature = "ser")]
impl Serialize for Gauge {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(1))?;
map.serialize_entry("value", &self.value())?;
map.end()
}
}
struct Cache<V> {
expiry: Instant,
value: V,
}
impl<V> Cache<V> {
fn expired(&self) -> bool {
self.expiry < Instant::now()
}
fn value(&self) -> &V {
&self.value
}
}
pub struct CachedGauge {
func: Box<dyn GaugeFn>,
cache: Mutex<Option<Cache<f64>>>,
ttl: Duration,
}
impl CachedGauge {
pub fn boxed(func: Box<dyn GaugeFn>, ttl: Duration) -> Box<CachedGauge> {
Box::new(CachedGauge {
func,
ttl,
cache: Mutex::new(None),
})
}
}
impl GaugeFn for CachedGauge {
fn value(&self) -> f64 {
let mut cache = self.cache.lock().unwrap();
if let Some(ref cache_inner) = *cache {
if !cache_inner.expired() {
return *cache_inner.value();
}
}
let value = self.func.value();
let new_cache = Cache {
expiry: Instant::now() + self.ttl,
value,
};
*cache = Some(new_cache);
value
}
}
pub struct StaticGauge(pub f64);
impl GaugeFn for StaticGauge {
fn value(&self) -> f64 {
self.0
}
}