use std::any::{Any, TypeId};
use std::cmp::Ordering;
pub trait AnyKey: Any + Send + Sync {
fn as_any(&self) -> &dyn Any;
fn cmp_dyn(&self, other: &dyn AnyKey) -> Ordering;
fn type_id_dyn(&self) -> TypeId;
}
impl<T: 'static + Ord + Any + Send + Sync> AnyKey for T {
fn as_any(&self) -> &dyn Any {
self
}
fn cmp_dyn(&self, other: &dyn AnyKey) -> Ordering {
if let Some(other_t) = other.as_any().downcast_ref::<T>() {
self.cmp(other_t)
} else {
panic!(
"Attempted to compare keys of different concrete types: {:?} vs {:?}",
self.type_id_dyn(),
other.type_id_dyn()
)
}
}
fn type_id_dyn(&self) -> TypeId {
TypeId::of::<T>()
}
}
pub struct AnyKeyBox(pub Box<dyn AnyKey>);
impl AnyKeyBox {
pub fn new<K: 'static + Ord + Any + Send + Sync>(k: K) -> Self {
AnyKeyBox(Box::new(k))
}
pub fn downcast_ref<K: 'static + Any>(&self) -> Option<&K> {
self.0.as_any().downcast_ref::<K>()
}
}
impl PartialEq for AnyKeyBox {
fn eq(&self, other: &Self) -> bool {
self.0.type_id_dyn() == other.0.type_id_dyn()
&& self.0.cmp_dyn(&*other.0) == Ordering::Equal
}
}
impl Eq for AnyKeyBox {}
impl PartialOrd for AnyKeyBox {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.0.type_id_dyn() == other.0.type_id_dyn() {
Some(self.0.cmp_dyn(&*other.0))
} else {
panic!(
"Attempted to compare keys of different concrete types: {:?} vs {:?}",
self.0.type_id_dyn(),
other.0.type_id_dyn()
)
}
}
}
impl Ord for AnyKeyBox {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::BTreeMap;
#[test]
fn test_dyn_key_box() {
let mut map: BTreeMap<AnyKeyBox, i64> = BTreeMap::new();
let k1 = AnyKeyBox::new((1u32, "one".to_string()));
map.insert(k1, 10);
let k3 = AnyKeyBox::new((1u32, "one".to_string()));
assert_eq!(Some(&10), map.get(&k3));
}
}