1use crate::component_registry::ComponentDefinition;
13use crate::instance_provider::ComponentInstanceAnyPtr;
14#[cfg(test)]
15use mockall::automock;
16use rustc_hash::FxHashMap;
17use std::any::TypeId;
18
19#[cfg(not(feature = "threadsafe"))]
20pub type ScopePtr = Box<dyn Scope>;
21#[cfg(feature = "threadsafe")]
22pub type ScopePtr = Box<dyn Scope + Send + Sync>;
23
24pub const SINGLETON: &str = "SINGLETON";
26
27pub const PROTOTYPE: &str = "PROTOTYPE";
29
30#[cfg_attr(test, automock)]
32pub trait Scope {
33 fn instance(&self, definition: &ComponentDefinition) -> Option<ComponentInstanceAnyPtr>;
35
36 fn store_instance(
39 &mut self,
40 definition: &ComponentDefinition,
41 instance: ComponentInstanceAnyPtr,
42 );
43}
44
45#[derive(Default)]
48pub struct SingletonScope {
49 instances: FxHashMap<TypeId, ComponentInstanceAnyPtr>,
50}
51
52impl Scope for SingletonScope {
53 #[inline]
54 fn instance(&self, definition: &ComponentDefinition) -> Option<ComponentInstanceAnyPtr> {
55 self.instances.get(&definition.resolved_type_id).cloned()
56 }
57
58 #[inline]
59 fn store_instance(
60 &mut self,
61 definition: &ComponentDefinition,
62 instance: ComponentInstanceAnyPtr,
63 ) {
64 self.instances.insert(definition.resolved_type_id, instance);
65 }
66}
67
68#[derive(Default, Copy, Clone, Eq, PartialEq)]
71pub struct PrototypeScope;
72
73impl Scope for PrototypeScope {
74 #[inline]
75 fn instance(&self, _definition: &ComponentDefinition) -> Option<ComponentInstanceAnyPtr> {
76 None
77 }
78
79 #[inline]
80 fn store_instance(
81 &mut self,
82 _definition: &ComponentDefinition,
83 _instance: ComponentInstanceAnyPtr,
84 ) {
85 }
86}
87
88#[cfg_attr(test, automock)]
90pub trait ScopeFactory {
91 fn create_scope(&self) -> ScopePtr;
92}
93
94#[derive(Copy, Clone, Eq, PartialEq, Default)]
95pub struct SingletonScopeFactory;
96
97impl ScopeFactory for SingletonScopeFactory {
98 fn create_scope(&self) -> ScopePtr {
99 Box::<SingletonScope>::default()
100 }
101}
102
103#[derive(Copy, Clone, Eq, PartialEq, Default)]
104pub struct PrototypeScopeFactory;
105
106impl ScopeFactory for PrototypeScopeFactory {
107 fn create_scope(&self) -> ScopePtr {
108 Box::<PrototypeScope>::default()
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 #[cfg(not(feature = "async"))]
115 mod sync {
116 use crate::component_registry::ComponentDefinition;
117 use crate::instance_provider::ComponentInstanceProviderError;
118 use crate::instance_provider::{
119 ComponentInstanceAnyPtr, ComponentInstanceProvider, ComponentInstancePtr,
120 };
121 use crate::scope::{PrototypeScopeFactory, ScopeFactory, SingletonScopeFactory};
122 use std::any::{type_name, Any, TypeId};
123
124 fn test_constructor(
125 _instance_provider: &mut dyn ComponentInstanceProvider,
126 ) -> Result<ComponentInstanceAnyPtr, ComponentInstanceProviderError> {
127 Err(ComponentInstanceProviderError::IncompatibleComponent {
128 type_id: TypeId::of::<i8>(),
129 type_name: type_name::<i8>().to_string(),
130 })
131 }
132
133 fn test_cast(
134 instance: ComponentInstanceAnyPtr,
135 ) -> Result<Box<dyn Any>, ComponentInstanceAnyPtr> {
136 Err(instance)
137 }
138
139 fn create_definition() -> ComponentDefinition {
140 ComponentDefinition {
141 names: Default::default(),
142 is_primary: false,
143 scope: "".to_string(),
144 resolved_type_id: TypeId::of::<u8>(),
145 resolved_type_name: type_name::<u8>().to_string(),
146 constructor: test_constructor,
147 cast: test_cast,
148 }
149 }
150
151 #[test]
152 fn should_support_singletons() {
153 let definition = create_definition();
154 let factory = SingletonScopeFactory;
155 let mut scope = factory.create_scope();
156
157 let instance = ComponentInstancePtr::new(0) as ComponentInstanceAnyPtr;
158 scope.store_instance(&definition, instance.clone());
159
160 assert!(scope.instance(&definition).is_some());
161 }
162
163 #[test]
164 fn should_support_prototypes() {
165 let definition = create_definition();
166 let factory = PrototypeScopeFactory;
167 let mut scope = factory.create_scope();
168
169 let instance = ComponentInstancePtr::new(0) as ComponentInstanceAnyPtr;
170 scope.store_instance(&definition, instance.clone());
171
172 assert!(scope.instance(&definition).is_none());
173 }
174 }
175}