| 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))); |
| } |
| } |