use std::any::Any;
use std::fmt::Debug;
pub trait MetricOptions: Any + Debug + Send + Sync {
#[doc(hidden)]
fn try_merge(&self, _other: &dyn MetricOptions) -> Option<MetricFlags<'static>> {
None
}
}
#[derive(Debug)]
pub struct Distribution;
impl MetricOptions for Distribution {}
#[derive(Copy, Clone, Debug)]
pub struct MetricFlags<'a>(Option<&'a dyn MetricOptions>);
impl<'a> MetricFlags<'a> {
pub const fn empty() -> Self {
Self(None)
}
pub const fn upcast<T: MetricOptions>(t: &'a T) -> Self {
MetricFlags(Some(t))
}
pub const fn merge_assert_none(&self, other: MetricFlags<'a>) -> Self {
assert!(self.0.is_none(), "flags can only be set once");
other
}
pub fn try_merge(&self, other: MetricFlags<'a>) -> Self {
match (self.0, other.0) {
(None, None) => Self::empty(),
(Some(_), None) => *self,
(None, Some(_)) => other,
(Some(f1), Some(f2)) => match f1.try_merge(f2) {
Some(merged) => merged,
None => panic!("unable to merge"),
},
}
}
pub fn downcast<T: MetricOptions>(&self) -> Option<&'a T> {
self.0.and_then(|x| (x as &dyn Any).downcast_ref())
}
}
#[cfg(test)]
mod test {
use std::any::Any;
use crate::value::MetricFlags;
#[test]
#[should_panic]
fn test_try_merge_panic() {
#[derive(Debug)]
struct MyOptions;
impl super::MetricOptions for MyOptions {}
let m = MetricFlags::upcast(&MyOptions);
m.merge_assert_none(m); }
#[test]
#[should_panic]
fn test_try_merge_matrix() {
#[derive(Debug)]
struct MyOptions {
order: u32,
}
impl super::MetricOptions for MyOptions {
fn try_merge(&self, other: &dyn super::MetricOptions) -> Option<MetricFlags<'static>> {
assert!(
self.order == 0
&& (other as &dyn Any)
.downcast_ref::<MyOptions>()
.unwrap()
.order
== 1
);
Some(&MyOptions { order: 2 });
None
}
}
let zero = MetricFlags::upcast(&MyOptions { order: 0 });
let one = MetricFlags::upcast(&MyOptions { order: 1 });
assert_eq!(
MetricFlags::empty()
.merge_assert_none(one)
.downcast::<MyOptions>()
.unwrap()
.order,
1
);
assert_eq!(
one.merge_assert_none(MetricFlags::empty())
.downcast::<MyOptions>()
.unwrap()
.order,
1
);
assert_eq!(
zero.merge_assert_none(one)
.downcast::<MyOptions>()
.unwrap()
.order,
2
);
}
}