use crate::util::AnyValueId; use crate::util::FlatMap; #[derive(Default, Clone, Debug)] pub(crate) struct Extensions { extensions: FlatMap<AnyValueId, BoxedExtension>, } impl Extensions { #[allow(dead_code)] pub(crate) fn get<T: Extension>(&self) -> Option<&T> { let id = AnyValueId::of::<T>(); self.extensions.get(&id).map(|e| e.as_ref::<T>()) } #[allow(dead_code)] pub(crate) fn get_mut<T: Extension>(&mut self) -> Option<&mut T> { let id = AnyValueId::of::<T>(); self.extensions.get_mut(&id).map(|e| e.as_mut::<T>()) } #[allow(dead_code)] pub(crate) fn get_or_insert_default<T: Extension + Default>(&mut self) -> &mut T { let id = AnyValueId::of::<T>(); self.extensions .entry(id) .or_insert_with(|| BoxedExtension::new(T::default())) .as_mut::<T>() } #[allow(dead_code)] pub(crate) fn set<T: Extension + Into<BoxedEntry>>(&mut self, tagged: T) -> bool { let BoxedEntry { id, value } = tagged.into(); self.extensions.insert(id, value).is_some() } #[allow(dead_code)] pub(crate) fn remove<T: Extension>(&mut self) -> Option<Box<dyn Extension>> { let id = AnyValueId::of::<T>(); self.extensions.remove(&id).map(BoxedExtension::into_inner) } pub(crate) fn update(&mut self, other: &Self) { for (key, value) in other.extensions.iter() { self.extensions.insert(*key, value.clone()); } } } /// Supports conversion to `Any`. Traits to be extended by `impl_downcast!` must extend `Extension`. pub(crate) trait Extension: std::fmt::Debug + Send + Sync + 'static { /// Convert `Box<dyn Trait>` (where `Trait: Extension`) to `Box<dyn Any>`. /// /// `Box<dyn Any>` can /// then be further `downcast` into /// `Box<ConcreteType>` where `ConcreteType` implements `Trait`. fn into_any(self: Box<Self>) -> Box<dyn std::any::Any>; /// Clone `&Box<dyn Trait>` (where `Trait: Extension`) to `Box<dyn Extension>`. /// /// `Box<dyn Any>` can /// then be further `downcast` into // `Box<ConcreteType>` where `ConcreteType` implements `Trait`. fn clone_extension(&self) -> Box<dyn Extension>; /// Convert `&Trait` (where `Trait: Extension`) to `&Any`. /// /// This is needed since Rust cannot /// generate `&Any`'s vtable from /// `&Trait`'s. fn as_any(&self) -> &dyn std::any::Any; /// Convert `&mut Trait` (where `Trait: Extension`) to `&Any`. /// /// This is needed since Rust cannot /// generate `&mut Any`'s vtable from /// `&mut Trait`'s. fn as_any_mut(&mut self) -> &mut dyn std::any::Any; } impl<T> Extension for T where T: Clone + std::fmt::Debug + Send + Sync + 'static, { fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> { self } fn clone_extension(&self) -> Box<dyn Extension> { Box::new(self.clone()) } fn as_any(&self) -> &dyn std::any::Any { self } fn as_any_mut(&mut self) -> &mut dyn std::any::Any { self } } impl Clone for Box<dyn Extension> { fn clone(&self) -> Self { self.as_ref().clone_extension() } } #[derive(Clone)] #[repr(transparent)] struct BoxedExtension(Box<dyn Extension>); impl BoxedExtension { fn new<T: Extension>(inner: T) -> Self { Self(Box::new(inner)) } fn into_inner(self) -> Box<dyn Extension> { self.0 } fn as_ref<T: Extension>(&self) -> &T { self.0.as_ref().as_any().downcast_ref::<T>().unwrap() } fn as_mut<T: Extension>(&mut self) -> &mut T { self.0.as_mut().as_any_mut().downcast_mut::<T>().unwrap() } } impl std::fmt::Debug for BoxedExtension { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { self.0.fmt(f) } } #[derive(Clone)] pub(crate) struct BoxedEntry { id: AnyValueId, value: BoxedExtension, } impl BoxedEntry { pub(crate) fn new(r: impl Extension) -> Self { let id = AnyValueId::from(&r); let value = BoxedExtension::new(r); BoxedEntry { id, value } } } impl<R: Extension> From<R> for BoxedEntry { fn from(inner: R) -> Self { BoxedEntry::new(inner) } } #[cfg(test)] mod test { use super::*; #[derive(Default, Copy, Clone, Debug, PartialEq, Eq)] struct Number(usize); #[test] fn get() { let mut ext = Extensions::default(); ext.set(Number(10)); assert_eq!(ext.get::<Number>(), Some(&Number(10))); } #[test] fn get_mut() { let mut ext = Extensions::default(); ext.set(Number(10)); *ext.get_mut::<Number>().unwrap() = Number(20); assert_eq!(ext.get::<Number>(), Some(&Number(20))); } #[test] fn get_or_insert_default_empty() { let mut ext = Extensions::default(); assert_eq!(ext.get_or_insert_default::<Number>(), &Number(0)); } #[test] fn get_or_insert_default_full() { let mut ext = Extensions::default(); ext.set(Number(10)); assert_eq!(ext.get_or_insert_default::<Number>(), &Number(10)); } #[test] fn set() { let mut ext = Extensions::default(); assert!(!ext.set(Number(10))); assert_eq!(ext.get::<Number>(), Some(&Number(10))); assert!(ext.set(Number(20))); assert_eq!(ext.get::<Number>(), Some(&Number(20))); } #[test] fn reset() { let mut ext = Extensions::default(); assert_eq!(ext.get::<Number>(), None); assert!(ext.remove::<Number>().is_none()); assert_eq!(ext.get::<Number>(), None); assert!(!ext.set(Number(10))); assert_eq!(ext.get::<Number>(), Some(&Number(10))); assert!(ext.remove::<Number>().is_some()); assert_eq!(ext.get::<Number>(), None); } #[test] fn update() { let mut ext = Extensions::default(); assert_eq!(ext.get::<Number>(), None); let mut new = Extensions::default(); assert!(!new.set(Number(10))); ext.update(&new); assert_eq!(ext.get::<Number>(), Some(&Number(10))); } }