use crate::component_registry::ComponentDefinition;
use crate::instance_provider::ComponentInstanceAnyPtr;
use fxhash::FxHashMap;
#[cfg(test)]
use mockall::automock;
use std::any::TypeId;
#[cfg(not(feature = "threadsafe"))]
pub type ScopePtr = Box<dyn Scope>;
#[cfg(feature = "threadsafe")]
pub type ScopePtr = Box<dyn Scope + Send + Sync>;
pub const SINGLETON: &str = "SINGLETON";
pub const PROTOTYPE: &str = "PROTOTYPE";
#[cfg_attr(test, automock)]
pub trait Scope {
fn instance(&self, definition: &ComponentDefinition) -> Option<ComponentInstanceAnyPtr>;
fn store_instance(
&mut self,
definition: &ComponentDefinition,
instance: ComponentInstanceAnyPtr,
);
}
#[derive(Default)]
pub struct SingletonScope {
instances: FxHashMap<TypeId, ComponentInstanceAnyPtr>,
}
impl Scope for SingletonScope {
#[inline]
fn instance(&self, definition: &ComponentDefinition) -> Option<ComponentInstanceAnyPtr> {
self.instances.get(&definition.resolved_type_id).cloned()
}
#[inline]
fn store_instance(
&mut self,
definition: &ComponentDefinition,
instance: ComponentInstanceAnyPtr,
) {
self.instances.insert(definition.resolved_type_id, instance);
}
}
#[derive(Default, Copy, Clone, Eq, PartialEq)]
pub struct PrototypeScope;
impl Scope for PrototypeScope {
#[inline]
fn instance(&self, _definition: &ComponentDefinition) -> Option<ComponentInstanceAnyPtr> {
None
}
#[inline]
fn store_instance(
&mut self,
_definition: &ComponentDefinition,
_instance: ComponentInstanceAnyPtr,
) {
}
}
#[cfg_attr(test, automock)]
pub trait ScopeFactory {
fn create_scope(&self) -> ScopePtr;
}
#[derive(Copy, Clone, Eq, PartialEq, Default)]
pub struct SingletonScopeFactory;
impl ScopeFactory for SingletonScopeFactory {
fn create_scope(&self) -> ScopePtr {
Box::<SingletonScope>::default()
}
}
#[derive(Copy, Clone, Eq, PartialEq, Default)]
pub struct PrototypeScopeFactory;
impl ScopeFactory for PrototypeScopeFactory {
fn create_scope(&self) -> ScopePtr {
Box::<PrototypeScope>::default()
}
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "async"))]
mod sync {
use crate::component_registry::ComponentDefinition;
use crate::instance_provider::ComponentInstanceProviderError;
use crate::instance_provider::{
ComponentInstanceAnyPtr, ComponentInstanceProvider, ComponentInstancePtr,
};
use crate::scope::{PrototypeScopeFactory, ScopeFactory, SingletonScopeFactory};
use std::any::{type_name, Any, TypeId};
fn test_constructor(
_instance_provider: &mut dyn ComponentInstanceProvider,
) -> Result<ComponentInstanceAnyPtr, ComponentInstanceProviderError> {
Err(ComponentInstanceProviderError::IncompatibleComponent(
TypeId::of::<i8>(),
))
}
fn test_cast(
instance: ComponentInstanceAnyPtr,
) -> Result<Box<dyn Any>, ComponentInstanceAnyPtr> {
Err(instance)
}
fn create_definition() -> ComponentDefinition {
ComponentDefinition {
names: Default::default(),
is_primary: false,
scope: "".to_string(),
resolved_type_id: TypeId::of::<u8>(),
resolved_type_name: type_name::<u8>().to_string(),
constructor: test_constructor,
cast: test_cast,
}
}
#[test]
fn should_support_singletons() {
let definition = create_definition();
let factory = SingletonScopeFactory;
let mut scope = factory.create_scope();
let instance = ComponentInstancePtr::new(0) as ComponentInstanceAnyPtr;
scope.store_instance(&definition, instance.clone());
assert!(scope.instance(&definition).is_some());
}
#[test]
fn should_support_prototypes() {
let definition = create_definition();
let factory = PrototypeScopeFactory;
let mut scope = factory.create_scope();
let instance = ComponentInstancePtr::new(0) as ComponentInstanceAnyPtr;
scope.store_instance(&definition, instance.clone());
assert!(scope.instance(&definition).is_none());
}
}
}