springtime_di/component_registry/
conditional.rs1use crate::component::Injectable;
4use crate::component_registry::{ComponentAliasMetadata, ComponentMetadata};
5#[cfg(test)]
6use mockall::automock;
7use std::any::TypeId;
8
9#[cfg_attr(test, automock)]
12pub trait ComponentDefinitionRegistryFacade {
13 fn is_registered(&self, target: TypeId) -> bool;
15
16 fn is_name_registered(&self, name: &str) -> bool;
18}
19
20pub trait Context {
22 fn registry(&self) -> &dyn ComponentDefinitionRegistryFacade;
24}
25
26pub trait ContextFactory {
28 fn create_context<'a>(
30 &self,
31 registry: &'a dyn ComponentDefinitionRegistryFacade,
32 ) -> Box<dyn Context + 'a>;
33}
34
35#[derive(Clone, Debug, Copy)]
37pub enum ConditionMetadata<'a> {
38 Component {
39 type_id: TypeId,
40 metadata: &'a ComponentMetadata,
41 },
42 Alias {
43 alias_type: TypeId,
44 target_type: TypeId,
45 metadata: &'a ComponentAliasMetadata,
46 },
47}
48
49pub type ComponentCondition = fn(context: &dyn Context, metadata: ConditionMetadata) -> bool;
51
52struct SimpleContext<'a> {
53 registry: &'a dyn ComponentDefinitionRegistryFacade,
54}
55
56impl Context for SimpleContext<'_> {
57 fn registry(&self) -> &dyn ComponentDefinitionRegistryFacade {
58 self.registry
59 }
60}
61
62#[derive(Default, Debug, Copy, Clone, Eq, PartialEq)]
64pub struct SimpleContextFactory;
65
66impl ContextFactory for SimpleContextFactory {
67 fn create_context<'a>(
68 &self,
69 registry: &'a dyn ComponentDefinitionRegistryFacade,
70 ) -> Box<dyn Context + 'a> {
71 Box::new(SimpleContext { registry })
72 }
73}
74
75pub fn registered_component<T: Injectable + ?Sized>(
77 context: &dyn Context,
78 _metadata: ConditionMetadata,
79) -> bool {
80 context.registry().is_registered(TypeId::of::<T>())
81}
82
83pub fn unregistered_component<T: Injectable + ?Sized>(
85 context: &dyn Context,
86 metadata: ConditionMetadata,
87) -> bool {
88 !registered_component::<T>(context, metadata)
89}
90
91pub fn unregistered_name(context: &dyn Context, metadata: ConditionMetadata) -> bool {
93 let registry = context.registry();
94 match metadata {
95 ConditionMetadata::Component {
96 metadata: ComponentMetadata { names, .. },
97 ..
98 } => !names.iter().any(|name| registry.is_name_registered(name)),
99 ConditionMetadata::Alias { .. } => true,
100 }
101}
102
103#[cfg(test)]
104mod tests {
105 #[cfg(not(feature = "async"))]
106 mod sync {
107 use crate::component::Injectable;
108 use crate::component_registry::conditional::{
109 registered_component, unregistered_component, unregistered_name, ConditionMetadata,
110 MockComponentDefinitionRegistryFacade, SimpleContext,
111 };
112 use crate::component_registry::{ComponentAliasMetadata, ComponentMetadata};
113 use crate::instance_provider::ComponentInstanceProviderError;
114 use crate::instance_provider::{ComponentInstanceAnyPtr, ComponentInstanceProvider};
115 use mockall::predicate::*;
116 use mockall::Sequence;
117 use std::any::{Any, TypeId};
118
119 struct TestComponent;
120
121 impl Injectable for TestComponent {}
122
123 fn test_constructor(
124 _instance_provider: &mut dyn ComponentInstanceProvider,
125 ) -> Result<ComponentInstanceAnyPtr, ComponentInstanceProviderError> {
126 Err(ComponentInstanceProviderError::NoNamedInstance(
127 "".to_string(),
128 ))
129 }
130
131 fn test_cast(
132 instance: ComponentInstanceAnyPtr,
133 ) -> Result<Box<dyn Any>, ComponentInstanceAnyPtr> {
134 Err(instance)
135 }
136
137 #[test]
138 fn should_check_for_component_existence() {
139 let mut seq = Sequence::new();
140
141 let mut registry = MockComponentDefinitionRegistryFacade::new();
142 registry
143 .expect_is_registered()
144 .with(eq(TypeId::of::<TestComponent>()))
145 .times(2)
146 .in_sequence(&mut seq)
147 .return_const(true);
148 registry
149 .expect_is_registered()
150 .with(eq(TypeId::of::<TestComponent>()))
151 .times(2)
152 .in_sequence(&mut seq)
153 .return_const(false);
154
155 let context = SimpleContext {
156 registry: ®istry,
157 };
158 let metadata = ComponentAliasMetadata {
159 is_primary: false,
160 scope: None,
161 cast: test_cast,
162 };
163 let metadata = ConditionMetadata::Alias {
164 alias_type: TypeId::of::<i8>(),
165 target_type: TypeId::of::<TestComponent>(),
166 metadata: &metadata,
167 };
168
169 assert!(registered_component::<TestComponent>(&context, metadata));
170 assert!(!unregistered_component::<TestComponent>(&context, metadata));
171 assert!(!registered_component::<TestComponent>(&context, metadata));
172 assert!(unregistered_component::<TestComponent>(&context, metadata));
173 }
174
175 #[test]
176 fn should_check_for_name_existence() {
177 let mut registry = MockComponentDefinitionRegistryFacade::new();
178 registry
179 .expect_is_name_registered()
180 .with(eq("n1"))
181 .times(1)
182 .return_const(true);
183 registry
184 .expect_is_name_registered()
185 .with(eq("n2"))
186 .times(1)
187 .return_const(false);
188
189 let context = SimpleContext {
190 registry: ®istry,
191 };
192
193 let metadata = ComponentMetadata {
194 names: ["n2".to_string(), "n1".to_string()].into_iter().collect(),
195 scope: "".to_string(),
196 constructor: test_constructor,
197 cast: test_cast,
198 };
199 let metadata = ConditionMetadata::Component {
200 type_id: TypeId::of::<TestComponent>(),
201 metadata: &metadata,
202 };
203
204 assert!(!unregistered_name(&context, metadata));
205
206 let metadata = ComponentAliasMetadata {
207 is_primary: false,
208 scope: None,
209 cast: test_cast,
210 };
211 let metadata = ConditionMetadata::Alias {
212 alias_type: TypeId::of::<i8>(),
213 target_type: TypeId::of::<TestComponent>(),
214 metadata: &metadata,
215 };
216
217 assert!(unregistered_name(&context, metadata));
218 }
219 }
220}