use std::any::TypeId; use ahash::AHashMap; use crate::di_container::BindingOptions; pub struct DIContainerBindingStorage where Provider: 'static + ?Sized, { inner: AHashMap, Box>, } impl DIContainerBindingStorage where Provider: 'static + ?Sized, { pub fn new() -> Self { Self { inner: AHashMap::new(), } } #[allow(clippy::borrowed_box)] pub fn get<'this, Interface>( &'this self, options: BindingOptions<'this>, ) -> Option<&'this Box> where Interface: 'static + ?Sized, { self.inner.get(&BindingId::new::(options)) } pub fn set( &mut self, options: BindingOptions<'static>, provider: Box, ) where Interface: 'static + ?Sized, { self.inner .insert(BindingId::new::(options), provider); } pub fn remove( &mut self, options: BindingOptions<'static>, ) -> Option> where Interface: 'static + ?Sized, { self.inner.remove(&BindingId::new::(options)) } pub fn has(&self, options: BindingOptions) -> bool where Interface: 'static + ?Sized, { self.inner .contains_key(&BindingId::new::(options)) } } impl Default for DIContainerBindingStorage where Provider: 'static + ?Sized, { fn default() -> Self { Self::new() } } #[derive(Debug, PartialEq, Eq, Hash)] struct BindingId<'opts> { type_id: TypeId, options: BindingOptions<'opts>, } impl<'opts> BindingId<'opts> { fn new(options: BindingOptions<'opts>) -> Self where Interface: ?Sized + 'static, { Self { type_id: TypeId::of::(), options, } } } #[cfg(test)] mod tests { use super::*; mod subjects { pub trait SomeProvider { fn get_id(&self) -> u8; } pub struct SomeProviderImpl { pub id: u8, } impl SomeProvider for SomeProviderImpl { fn get_id(&self) -> u8 { self.id } } } #[test] fn can_get() { type Interface = (); let mut binding_map = DIContainerBindingStorage::::new(); binding_map.inner.insert( BindingId::new::(BindingOptions::new()), Box::new(subjects::SomeProviderImpl { id: 20 }), ); assert!(binding_map .get::(BindingOptions::new()) .map_or_else(|| false, |provider| provider.get_id() == 20)); } #[test] fn can_get_with_name() { type Interface = (); let mut binding_map = DIContainerBindingStorage::::new(); binding_map.inner.insert( BindingId::new::(BindingOptions::new().name("hello")), Box::new(subjects::SomeProviderImpl { id: 11 }), ); assert!(binding_map .get::(BindingOptions::new().name("hello")) .map_or_else(|| false, |provider| provider.get_id() == 11)); assert!(binding_map .get::(BindingOptions::new()) .is_none()); } #[test] fn can_set() { type Interface = (); let mut binding_map = DIContainerBindingStorage::::new(); binding_map.set::( BindingOptions::new(), Box::new(subjects::SomeProviderImpl { id: 65 }), ); let expected_key = BindingId::new::(BindingOptions::new()); assert!(binding_map.inner.contains_key(&expected_key)); assert_eq!(binding_map.inner[&expected_key].get_id(), 65); } #[test] fn can_set_with_name() { type Interface = (); let mut binding_map = DIContainerBindingStorage::::new(); binding_map.set::( BindingOptions::new().name("special"), Box::new(subjects::SomeProviderImpl { id: 3 }), ); let expected_key = BindingId::new::(BindingOptions::new().name("special")); assert!(binding_map.inner.contains_key(&expected_key)); assert_eq!(binding_map.inner[&expected_key].get_id(), 3); } #[test] fn can_remove() { type Interface = (); let mut binding_map = DIContainerBindingStorage::::new(); binding_map.inner.insert( BindingId::new::(BindingOptions::new()), Box::new(subjects::SomeProviderImpl { id: 103 }), ); binding_map.remove::(BindingOptions::new()); assert!(!binding_map .inner .contains_key(&BindingId::new::(BindingOptions::new()))); } #[test] fn can_remove_with_name() { type Interface = (); let mut binding_map = DIContainerBindingStorage::::new(); binding_map.inner.insert( BindingId::new::(BindingOptions::new().name("cool")), Box::new(subjects::SomeProviderImpl { id: 42 }), ); binding_map.remove::(BindingOptions::new().name("cool")); assert!( !binding_map.inner.contains_key(&BindingId::new::( BindingOptions::new().name("cool") )) ); } #[test] fn can_get_has() { type Interface = (); let mut binding_map = DIContainerBindingStorage::::new(); assert!(!binding_map.has::(BindingOptions::new())); binding_map.inner.insert( BindingId::new::(BindingOptions::new()), Box::new(subjects::SomeProviderImpl { id: 103 }), ); assert!(binding_map.has::(BindingOptions::new())); } #[test] fn can_get_has_with_name() { type Interface = (); let mut binding_map = DIContainerBindingStorage::::new(); assert!(!binding_map.has::(BindingOptions::new().name("awesome"))); binding_map.inner.insert( BindingId::new::(BindingOptions::new().name("awesome")), Box::new(subjects::SomeProviderImpl { id: 101 }), ); assert!(binding_map.has::(BindingOptions::new().name("awesome"))); } }