use crate::Metric;
use once_cell::sync::OnceCell;
use std::cell::Cell;
use std::ops::{Deref, DerefMut};
pub struct Lazy<T, F = fn() -> T> {
cell: OnceCell<T>,
func: Cell<Option<F>>,
}
unsafe impl<T, F: Send> Sync for Lazy<T, F> where OnceCell<T>: Sync {}
impl<T, F> Lazy<T, F> {
pub const fn new(func: F) -> Self {
Self {
cell: OnceCell::new(),
func: Cell::new(Some(func)),
}
}
pub fn get(this: &Self) -> Option<&T> {
this.cell.get()
}
pub fn get_mut(this: &mut Self) -> Option<&mut T> {
this.cell.get_mut()
}
}
impl<T, F: FnOnce() -> T> Lazy<T, F> {
pub fn force(this: &Self) -> &T {
this.cell.get_or_init(|| {
let func = this
.func
.take()
.unwrap_or_else(|| panic!("Lazy instance has previously been poisoned"));
func()
})
}
}
impl<T, F: FnOnce() -> T> Deref for Lazy<T, F> {
type Target = T;
fn deref(&self) -> &Self::Target {
Self::force(self)
}
}
impl<T, F: FnOnce() -> T> DerefMut for Lazy<T, F> {
fn deref_mut(&mut self) -> &mut Self::Target {
Self::force(self);
self.cell.get_mut().unwrap_or_else(|| unreachable!())
}
}
impl<T: Default> Default for Lazy<T> {
fn default() -> Self {
Self::new(T::default)
}
}
impl<T: Metric, F: Send + 'static> Metric for Lazy<T, F> {
fn is_enabled(&self) -> bool {
Lazy::get(self).is_some()
}
fn as_any(&self) -> Option<&dyn std::any::Any> {
match Lazy::get(self) {
Some(metric) => Some(metric),
None => None,
}
}
fn value(&self) -> Option<crate::Value> {
Lazy::get(self).and_then(|metric| metric.value())
}
}