1use crate::component::Injectable;
5#[cfg(feature = "async")]
6use futures::future::BoxFuture;
7#[cfg(feature = "async")]
8use futures::FutureExt;
9use itertools::Itertools;
10#[cfg(test)]
11use mockall::automock;
12use std::any::{type_name, Any, TypeId};
13use std::error::Error;
14#[cfg(not(feature = "threadsafe"))]
15use std::rc::Rc;
16#[cfg(feature = "threadsafe")]
17use std::sync::Arc;
18use thiserror::Error;
19
20#[cfg(not(feature = "threadsafe"))]
21pub type ErrorPtr = Rc<dyn Error>;
22#[cfg(feature = "threadsafe")]
23pub type ErrorPtr = Arc<dyn Error + Send + Sync>;
24
25#[derive(Error, Debug, Clone)]
27pub enum ComponentInstanceProviderError {
28 #[error("Cannot find a primary instance for component '{type_id:?}/{type_name:?}' - either none or multiple exists without a primary marker.")]
31 NoPrimaryInstance {
32 type_id: TypeId,
33 type_name: Option<String>,
34 },
35 #[error("Tried to downcast component to incompatible type: {type_id:?}/{type_name}")]
37 IncompatibleComponent { type_id: TypeId, type_name: String },
38 #[error("Cannot find named component: {0}")]
40 NoNamedInstance(String),
41 #[error("Unrecognized scope: {0}")]
44 UnrecognizedScope(String),
45 #[error("Detected dependency cycle for: {type_id:?}/{type_name:?}")]
46 DependencyCycle {
48 type_id: TypeId,
49 type_name: Option<String>,
50 },
51 #[error("Error in component constructor: {0}")]
53 ConstructorError(#[source] ErrorPtr),
54}
55
56#[cfg(not(feature = "threadsafe"))]
57pub type ComponentInstancePtr<T> = Rc<T>;
58#[cfg(feature = "threadsafe")]
59pub type ComponentInstancePtr<T> = Arc<T>;
60
61#[cfg(not(feature = "threadsafe"))]
62pub type ComponentInstanceAnyPtr = ComponentInstancePtr<dyn Any + 'static>;
63#[cfg(feature = "threadsafe")]
64pub type ComponentInstanceAnyPtr = ComponentInstancePtr<dyn Any + Send + Sync + 'static>;
65
66pub type CastFunction =
71 fn(instance: ComponentInstanceAnyPtr) -> Result<Box<dyn Any>, ComponentInstanceAnyPtr>;
72
73#[cfg(feature = "async")]
75#[cfg_attr(test, automock)]
76pub trait ComponentInstanceProvider {
77 fn primary_instance(
80 &mut self,
81 type_id: TypeId,
82 ) -> BoxFuture<
83 '_,
84 Result<(ComponentInstanceAnyPtr, CastFunction), ComponentInstanceProviderError>,
85 >;
86
87 fn instances(
91 &mut self,
92 type_id: TypeId,
93 ) -> BoxFuture<
94 '_,
95 Result<Vec<(ComponentInstanceAnyPtr, CastFunction)>, ComponentInstanceProviderError>,
96 >;
97
98 fn instance_by_name(
100 &mut self,
101 name: &str,
102 type_id: TypeId,
103 ) -> BoxFuture<
104 '_,
105 Result<(ComponentInstanceAnyPtr, CastFunction), ComponentInstanceProviderError>,
106 >;
107}
108
109#[cfg(not(feature = "async"))]
110#[cfg_attr(test, automock)]
111pub trait ComponentInstanceProvider {
112 fn primary_instance(
115 &mut self,
116 type_id: TypeId,
117 ) -> Result<(ComponentInstanceAnyPtr, CastFunction), ComponentInstanceProviderError>;
118
119 fn instances(
123 &mut self,
124 type_id: TypeId,
125 ) -> Result<Vec<(ComponentInstanceAnyPtr, CastFunction)>, ComponentInstanceProviderError>;
126
127 fn instance_by_name(
129 &mut self,
130 name: &str,
131 type_id: TypeId,
132 ) -> Result<(ComponentInstanceAnyPtr, CastFunction), ComponentInstanceProviderError>;
133}
134
135#[cfg(feature = "async")]
137pub trait TypedComponentInstanceProvider {
138 fn primary_instance_typed<T: Injectable + ?Sized>(
140 &mut self,
141 ) -> BoxFuture<'_, Result<ComponentInstancePtr<T>, ComponentInstanceProviderError>>;
142
143 fn primary_instance_option<T: Injectable + ?Sized>(
146 &mut self,
147 ) -> BoxFuture<'_, Result<Option<ComponentInstancePtr<T>>, ComponentInstanceProviderError>>;
148
149 fn instances_typed<T: Injectable + ?Sized>(
151 &mut self,
152 ) -> BoxFuture<'_, Result<Vec<ComponentInstancePtr<T>>, ComponentInstanceProviderError>>;
153
154 fn instance_by_name_typed<T: Injectable + ?Sized>(
156 &mut self,
157 name: &str,
158 ) -> BoxFuture<'_, Result<ComponentInstancePtr<T>, ComponentInstanceProviderError>>;
159
160 fn instance_by_name_option<T: Injectable + ?Sized>(
163 &mut self,
164 name: &str,
165 ) -> BoxFuture<'_, Result<Option<ComponentInstancePtr<T>>, ComponentInstanceProviderError>>;
166}
167
168#[cfg(not(feature = "async"))]
170pub trait TypedComponentInstanceProvider {
171 fn primary_instance_typed<T: Injectable + ?Sized>(
173 &mut self,
174 ) -> Result<ComponentInstancePtr<T>, ComponentInstanceProviderError>;
175
176 fn primary_instance_option<T: Injectable + ?Sized>(
179 &mut self,
180 ) -> Result<Option<ComponentInstancePtr<T>>, ComponentInstanceProviderError>;
181
182 fn instances_typed<T: Injectable + ?Sized>(
184 &mut self,
185 ) -> Result<Vec<ComponentInstancePtr<T>>, ComponentInstanceProviderError>;
186
187 fn instance_by_name_typed<T: Injectable + ?Sized>(
189 &mut self,
190 name: &str,
191 ) -> Result<ComponentInstancePtr<T>, ComponentInstanceProviderError>;
192
193 fn instance_by_name_option<T: Injectable + ?Sized>(
196 &mut self,
197 name: &str,
198 ) -> Result<Option<ComponentInstancePtr<T>>, ComponentInstanceProviderError>;
199}
200
201#[cfg(feature = "async")]
203impl<CIP: ComponentInstanceProvider + ?Sized + Sync + Send> TypedComponentInstanceProvider for CIP {
204 fn primary_instance_typed<T: Injectable + ?Sized>(
205 &mut self,
206 ) -> BoxFuture<'_, Result<ComponentInstancePtr<T>, ComponentInstanceProviderError>> {
207 async {
208 let type_id = TypeId::of::<T>();
209 self.primary_instance(type_id)
210 .await
211 .and_then(move |(p, cast)| cast_instance(p, cast, type_id))
212 .map_err(|error| enrich_error::<T>(error))
213 }
214 .boxed()
215 }
216
217 fn primary_instance_option<T: Injectable + ?Sized>(
218 &mut self,
219 ) -> BoxFuture<'_, Result<Option<ComponentInstancePtr<T>>, ComponentInstanceProviderError>>
220 {
221 async {
222 match self.primary_instance_typed::<T>().await {
223 Ok(ptr) => Ok(Some(ptr)),
224 Err(ComponentInstanceProviderError::NoPrimaryInstance { .. }) => Ok(None),
225 Err(error) => Err(enrich_error::<T>(error)),
226 }
227 }
228 .boxed()
229 }
230
231 fn instances_typed<T: Injectable + ?Sized>(
232 &mut self,
233 ) -> BoxFuture<'_, Result<Vec<ComponentInstancePtr<T>>, ComponentInstanceProviderError>> {
234 async {
235 let type_id = TypeId::of::<T>();
236 self.instances(type_id)
237 .await
238 .and_then(|instances| {
239 instances
240 .into_iter()
241 .map(move |(p, cast)| cast_instance(p, cast, type_id))
242 .try_collect()
243 })
244 .map_err(|error| enrich_error::<T>(error))
245 }
246 .boxed()
247 }
248
249 fn instance_by_name_typed<T: Injectable + ?Sized>(
250 &mut self,
251 name: &str,
252 ) -> BoxFuture<'_, Result<ComponentInstancePtr<T>, ComponentInstanceProviderError>> {
253 let name = name.to_string();
254 async move {
255 let type_id = TypeId::of::<T>();
256 self.instance_by_name(&name, type_id)
257 .await
258 .and_then(move |(p, cast)| cast_instance(p, cast, type_id))
259 .map_err(|error| enrich_error::<T>(error))
260 }
261 .boxed()
262 }
263
264 fn instance_by_name_option<T: Injectable + ?Sized>(
265 &mut self,
266 name: &str,
267 ) -> BoxFuture<'_, Result<Option<ComponentInstancePtr<T>>, ComponentInstanceProviderError>>
268 {
269 let name = name.to_string();
270 async move {
271 match self.instance_by_name_typed::<T>(&name).await {
272 Ok(ptr) => Ok(Some(ptr)),
273 Err(ComponentInstanceProviderError::NoPrimaryInstance { .. }) => Ok(None),
274 Err(error) => Err(enrich_error::<T>(error)),
275 }
276 }
277 .boxed()
278 }
279}
280
281#[cfg(not(feature = "async"))]
283impl<CIP: ComponentInstanceProvider + ?Sized> TypedComponentInstanceProvider for CIP {
284 fn primary_instance_typed<T: Injectable + ?Sized>(
285 &mut self,
286 ) -> Result<ComponentInstancePtr<T>, ComponentInstanceProviderError> {
287 let type_id = TypeId::of::<T>();
288 self.primary_instance(type_id)
289 .and_then(move |(p, cast)| cast_instance(p, cast, type_id))
290 .map_err(|error| enrich_error::<T>(error))
291 }
292
293 fn primary_instance_option<T: Injectable + ?Sized>(
294 &mut self,
295 ) -> Result<Option<ComponentInstancePtr<T>>, ComponentInstanceProviderError> {
296 match self.primary_instance_typed::<T>() {
297 Ok(ptr) => Ok(Some(ptr)),
298 Err(ComponentInstanceProviderError::NoPrimaryInstance { .. }) => Ok(None),
299 Err(error) => Err(enrich_error::<T>(error)),
300 }
301 }
302
303 fn instances_typed<T: Injectable + ?Sized>(
304 &mut self,
305 ) -> Result<Vec<ComponentInstancePtr<T>>, ComponentInstanceProviderError> {
306 let type_id = TypeId::of::<T>();
307 self.instances(type_id)
308 .and_then(|instances| {
309 instances
310 .into_iter()
311 .map(move |(p, cast)| cast_instance(p, cast, type_id))
312 .try_collect()
313 })
314 .map_err(|error| enrich_error::<T>(error))
315 }
316
317 fn instance_by_name_typed<T: Injectable + ?Sized>(
318 &mut self,
319 name: &str,
320 ) -> Result<ComponentInstancePtr<T>, ComponentInstanceProviderError> {
321 let type_id = TypeId::of::<T>();
322 self.instance_by_name(name, type_id)
323 .and_then(move |(p, cast)| cast_instance(p, cast, type_id))
324 .map_err(|error| enrich_error::<T>(error))
325 }
326
327 fn instance_by_name_option<T: Injectable + ?Sized>(
328 &mut self,
329 name: &str,
330 ) -> Result<Option<ComponentInstancePtr<T>>, ComponentInstanceProviderError> {
331 match self.instance_by_name_typed::<T>(name) {
332 Ok(ptr) => Ok(Some(ptr)),
333 Err(ComponentInstanceProviderError::NoPrimaryInstance { .. }) => Ok(None),
334 Err(error) => Err(enrich_error::<T>(error)),
335 }
336 }
337}
338
339fn enrich_error<T: ?Sized>(
340 error: ComponentInstanceProviderError,
341) -> ComponentInstanceProviderError {
342 match error {
343 ComponentInstanceProviderError::NoPrimaryInstance {
344 type_id,
345 type_name: None,
346 } => ComponentInstanceProviderError::NoPrimaryInstance {
347 type_id,
348 type_name: Some(type_name::<T>().to_string()),
349 },
350 ComponentInstanceProviderError::DependencyCycle {
351 type_id,
352 type_name: None,
353 } => ComponentInstanceProviderError::DependencyCycle {
354 type_id,
355 type_name: Some(type_name::<T>().to_string()),
356 },
357 _ => error,
358 }
359}
360
361fn cast_instance<T: Injectable + ?Sized>(
362 instance: ComponentInstanceAnyPtr,
363 cast: CastFunction,
364 type_id: TypeId,
365) -> Result<ComponentInstancePtr<T>, ComponentInstanceProviderError> {
366 debug_assert_eq!(type_id, TypeId::of::<T>());
367 cast(instance)
368 .map_err(|_| ComponentInstanceProviderError::IncompatibleComponent {
369 type_id,
370 type_name: type_name::<T>().to_string(),
371 })
372 .and_then(|p| {
373 p.downcast::<ComponentInstancePtr<T>>()
374 .map(|p| (*p).clone())
375 .map_err(|_| ComponentInstanceProviderError::IncompatibleComponent {
376 type_id,
377 type_name: type_name::<T>().to_string(),
378 })
379 })
380}
381
382#[cfg(test)]
383mod tests {
385 #[cfg(not(feature = "async"))]
386 mod sync {
387 use crate::component::Injectable;
388 use crate::instance_provider::{
389 CastFunction, ComponentInstanceAnyPtr, ComponentInstancePtr,
390 MockComponentInstanceProvider, TypedComponentInstanceProvider,
391 };
392 use mockall::predicate::*;
393 use std::any::{Any, TypeId};
394
395 struct TestComponent;
396
397 impl Injectable for TestComponent {}
398
399 fn test_cast(
400 instance: ComponentInstanceAnyPtr,
401 ) -> Result<Box<dyn Any>, ComponentInstanceAnyPtr> {
402 instance
403 .downcast::<TestComponent>()
404 .map(|p| Box::new(p) as Box<dyn Any>)
405 }
406
407 #[test]
408 fn should_provide_primary_instance_typed() {
409 let mut instance_provider = MockComponentInstanceProvider::new();
410 instance_provider
411 .expect_primary_instance()
412 .with(eq(TypeId::of::<TestComponent>()))
413 .times(1)
414 .return_const(Ok((
415 ComponentInstancePtr::new(TestComponent) as ComponentInstanceAnyPtr,
416 test_cast as CastFunction,
417 )));
418
419 assert!(instance_provider
420 .primary_instance_typed::<TestComponent>()
421 .is_ok());
422 }
423
424 #[test]
425 fn should_provide_primary_instance_option() {
426 let mut instance_provider = MockComponentInstanceProvider::new();
427 instance_provider
428 .expect_primary_instance()
429 .with(eq(TypeId::of::<TestComponent>()))
430 .times(1)
431 .return_const(Ok((
432 ComponentInstancePtr::new(TestComponent) as ComponentInstanceAnyPtr,
433 test_cast as CastFunction,
434 )));
435
436 assert!(instance_provider
437 .primary_instance_option::<TestComponent>()
438 .unwrap()
439 .is_some());
440 }
441
442 #[test]
443 fn should_provide_instances_typed() {
444 let mut instance_provider = MockComponentInstanceProvider::new();
445 instance_provider
446 .expect_instances()
447 .with(eq(TypeId::of::<TestComponent>()))
448 .times(1)
449 .return_const(Ok(vec![(
450 ComponentInstancePtr::new(TestComponent) as ComponentInstanceAnyPtr,
451 test_cast as CastFunction,
452 )]));
453
454 assert!(!instance_provider
455 .instances_typed::<TestComponent>()
456 .unwrap()
457 .is_empty());
458 }
459
460 #[test]
461 fn should_provide_instance_by_name_typed() {
462 let name = "name";
463
464 let mut instance_provider = MockComponentInstanceProvider::new();
465 instance_provider
466 .expect_instance_by_name()
467 .with(eq(name), eq(TypeId::of::<TestComponent>()))
468 .times(1)
469 .return_const(Ok((
470 ComponentInstancePtr::new(TestComponent) as ComponentInstanceAnyPtr,
471 test_cast as CastFunction,
472 )));
473
474 assert!(instance_provider
475 .instance_by_name_typed::<TestComponent>(name)
476 .is_ok());
477 }
478
479 #[test]
480 fn should_provide_instance_by_name_option() {
481 let name = "name";
482
483 let mut instance_provider = MockComponentInstanceProvider::new();
484 instance_provider
485 .expect_instance_by_name()
486 .with(eq(name), eq(TypeId::of::<TestComponent>()))
487 .times(1)
488 .return_const(Ok((
489 ComponentInstancePtr::new(TestComponent) as ComponentInstanceAnyPtr,
490 test_cast as CastFunction,
491 )));
492
493 assert!(instance_provider
494 .instance_by_name_option::<TestComponent>(name)
495 .unwrap()
496 .is_some());
497 }
498 }
499}