use crate::component::Injectable;
use crate::component_registry::{ComponentAliasMetadata, ComponentMetadata};
#[cfg(test)]
use mockall::automock;
use std::any::TypeId;
#[cfg_attr(test, automock)]
pub trait ComponentDefinitionRegistryFacade {
fn is_registered(&self, target: TypeId) -> bool;
fn is_name_registered(&self, name: &str) -> bool;
}
pub trait Context {
fn registry(&self) -> &dyn ComponentDefinitionRegistryFacade;
}
pub trait ContextFactory {
fn create_context<'a>(
&self,
registry: &'a dyn ComponentDefinitionRegistryFacade,
) -> Box<dyn Context + 'a>;
}
#[derive(Clone, Debug, Copy)]
pub enum ConditionMetadata<'a> {
Component {
type_id: TypeId,
metadata: &'a ComponentMetadata,
},
Alias {
alias_type: TypeId,
target_type: TypeId,
metadata: &'a ComponentAliasMetadata,
},
}
pub type ComponentCondition = fn(context: &dyn Context, metadata: ConditionMetadata) -> bool;
struct SimpleContext<'a> {
registry: &'a dyn ComponentDefinitionRegistryFacade,
}
impl Context for SimpleContext<'_> {
fn registry(&self) -> &dyn ComponentDefinitionRegistryFacade {
self.registry
}
}
#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
pub struct SimpleContextFactory;
impl ContextFactory for SimpleContextFactory {
fn create_context<'a>(
&self,
registry: &'a dyn ComponentDefinitionRegistryFacade,
) -> Box<dyn Context + 'a> {
Box::new(SimpleContext { registry })
}
}
pub fn registered_component<T: Injectable + ?Sized>(
context: &dyn Context,
_metadata: ConditionMetadata,
) -> bool {
context.registry().is_registered(TypeId::of::<T>())
}
pub fn unregistered_component<T: Injectable + ?Sized>(
context: &dyn Context,
metadata: ConditionMetadata,
) -> bool {
!registered_component::<T>(context, metadata)
}
pub fn unregistered_name(context: &dyn Context, metadata: ConditionMetadata) -> bool {
let registry = context.registry();
match metadata {
ConditionMetadata::Component {
metadata: ComponentMetadata { names, .. },
..
} => !names.iter().any(|name| registry.is_name_registered(name)),
ConditionMetadata::Alias { .. } => true,
}
}
#[cfg(test)]
mod tests {
#[cfg(not(feature = "async"))]
mod sync {
use crate::component::Injectable;
use crate::component_registry::conditional::{
registered_component, unregistered_component, unregistered_name, ConditionMetadata,
MockComponentDefinitionRegistryFacade, SimpleContext,
};
use crate::component_registry::{ComponentAliasMetadata, ComponentMetadata};
use crate::instance_provider::ComponentInstanceProviderError;
use crate::instance_provider::{ComponentInstanceAnyPtr, ComponentInstanceProvider};
use mockall::predicate::*;
use mockall::Sequence;
use std::any::{Any, TypeId};
struct TestComponent;
impl Injectable for TestComponent {}
fn test_constructor(
_instance_provider: &mut dyn ComponentInstanceProvider,
) -> Result<ComponentInstanceAnyPtr, ComponentInstanceProviderError> {
Err(ComponentInstanceProviderError::NoNamedInstance(
"".to_string(),
))
}
fn test_cast(
instance: ComponentInstanceAnyPtr,
) -> Result<Box<dyn Any>, ComponentInstanceAnyPtr> {
Err(instance)
}
#[test]
fn should_check_for_component_existence() {
let mut seq = Sequence::new();
let mut registry = MockComponentDefinitionRegistryFacade::new();
registry
.expect_is_registered()
.with(eq(TypeId::of::<TestComponent>()))
.times(2)
.in_sequence(&mut seq)
.return_const(true);
registry
.expect_is_registered()
.with(eq(TypeId::of::<TestComponent>()))
.times(2)
.in_sequence(&mut seq)
.return_const(false);
let context = SimpleContext {
registry: ®istry,
};
let metadata = ComponentAliasMetadata {
is_primary: false,
scope: None,
cast: test_cast,
};
let metadata = ConditionMetadata::Alias {
alias_type: TypeId::of::<i8>(),
target_type: TypeId::of::<TestComponent>(),
metadata: &metadata,
};
assert!(registered_component::<TestComponent>(&context, metadata));
assert!(!unregistered_component::<TestComponent>(&context, metadata));
assert!(!registered_component::<TestComponent>(&context, metadata));
assert!(unregistered_component::<TestComponent>(&context, metadata));
}
#[test]
fn should_check_for_name_existence() {
let mut registry = MockComponentDefinitionRegistryFacade::new();
registry
.expect_is_name_registered()
.with(eq("n1"))
.times(1)
.return_const(true);
registry
.expect_is_name_registered()
.with(eq("n2"))
.times(1)
.return_const(false);
let context = SimpleContext {
registry: ®istry,
};
let metadata = ComponentMetadata {
names: ["n2".to_string(), "n1".to_string()].into_iter().collect(),
scope: "".to_string(),
constructor: test_constructor,
cast: test_cast,
};
let metadata = ConditionMetadata::Component {
type_id: TypeId::of::<TestComponent>(),
metadata: &metadata,
};
assert!(!unregistered_name(&context, metadata));
let metadata = ComponentAliasMetadata {
is_primary: false,
scope: None,
cast: test_cast,
};
let metadata = ConditionMetadata::Alias {
alias_type: TypeId::of::<i8>(),
target_type: TypeId::of::<TestComponent>(),
metadata: &metadata,
};
assert!(unregistered_name(&context, metadata));
}
}
}