use serde::Serialize;
use serde_value::Value;
use std::any::TypeId;
use std::sync::Arc;
mod private {
pub struct PrivacyToken;
}
pub trait Gauge: 'static + Sync + Send {
fn value(&self) -> Value;
#[doc(hidden)]
fn __private_api_type_id(&self, _: private::PrivacyToken) -> TypeId {
TypeId::of::<Self>()
}
}
impl dyn Gauge {
pub fn is<T>(&self) -> bool
where
T: Gauge,
{
self.__private_api_type_id(private::PrivacyToken) == TypeId::of::<T>()
}
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: Gauge,
{
if self.is::<T>() {
unsafe { Some(&*(self as *const dyn Gauge as *const T)) }
} else {
None
}
}
pub fn downcast_arc<T>(self: Arc<Self>) -> Result<Arc<T>, Arc<Self>>
where
T: Gauge,
{
if self.is::<T>() {
unsafe { Ok(Arc::from_raw(Arc::into_raw(self).cast::<T>())) }
} else {
Err(self)
}
}
}
impl<F, R> Gauge for F
where
F: Fn() -> R + 'static + Sync + Send,
R: Serialize,
{
fn value(&self) -> Value {
serde_value::to_value(self()).expect("value failed to serialize")
}
}
#[cfg(test)]
mod test {
use super::*;
struct TestGauge {
value: i64,
}
impl Gauge for TestGauge {
fn value(&self) -> Value {
Value::I64(self.value)
}
}
#[test]
fn downcast() {
let gauge: Arc<dyn Gauge> = Arc::new(TestGauge { value: 42 });
assert!(gauge.is::<TestGauge>());
assert!(!gauge.is::<fn() -> Value>());
assert!(gauge.downcast_ref::<fn() -> Value>().is_none());
assert_eq!(gauge.downcast_ref::<TestGauge>().unwrap().value, 42);
assert!(gauge.clone().downcast_arc::<fn() -> Value>().is_err());
assert_eq!(gauge.downcast_arc::<TestGauge>().ok().unwrap().value, 42);
}
}