sword_core/injectables/container.rs
1#![allow(clippy::type_complexity)]
2
3use crate::{ComponentRegistry, DependencyInjectionError as DIError, ProviderRegistry, State};
4
5use std::{any::TypeId, collections::HashSet, sync::Arc};
6
7/// A container for managing dependencies and their builders.
8///
9/// It support two types of registrations:
10///
11/// **Providers**:
12///
13/// Providers are pre-created objects that you want to register directly into the container.
14/// For example, you might have a database connection or external service client that you
15/// need to build beforehand and inject into other Dependencies.
16///
17/// **Components**
18///
19/// Are types that has no need to be pre-created. Instead, you register the type itself,
20/// and the container will use the `Component` trait to build them when needed, resolving
21/// their dependencies automatically.
22pub struct DependencyContainer {
23 providers: ProviderRegistry,
24 components: ComponentRegistry,
25}
26
27impl DependencyContainer {
28 pub fn new() -> Self {
29 Self {
30 providers: ProviderRegistry::new(),
31 components: ComponentRegistry::new(),
32 }
33 }
34
35 pub fn provider_registry(&self) -> &ProviderRegistry {
36 &self.providers
37 }
38
39 pub fn component_registry(&self) -> &ComponentRegistry {
40 &self.components
41 }
42
43 /// Builds all registered components in dependency order.
44 ///
45 /// This internal method performs the following steps:
46 /// 1. Registers all provider instances in the State
47 /// 2. Performs topological sorting on the dependency graph
48 /// 3. Constructs components recursively in the correct order
49 /// 4. Detects circular dependencies and returns an error if found
50 ///
51 /// This method is called internally during application initialization.
52 pub fn build_all(&self, state: &State) -> Result<(), DIError> {
53 let mut built = HashSet::new();
54 let mut visiting = HashSet::new();
55
56 // First. register all the provided instances
57
58 let providers = &self.providers.get_providers();
59
60 for (type_id, instance) in providers.read().iter() {
61 state.insert_instance(*type_id, Arc::clone(instance));
62 built.insert(*type_id);
63 }
64
65 // Then, build the rest based on dependencies in topological order.
66 // If a type_id is already built, skip it (Dep already built).
67
68 for type_id in self.components.get_dependency_graph().read().keys() {
69 self.build_recursive(type_id, state, &mut built, &mut visiting)?;
70 }
71
72 Ok(())
73 }
74
75 /// Recursively builds a component and its dependencies.
76 ///
77 /// This method implements depth-first traversal of the dependency graph:
78 /// - Skips already built components
79 /// - Detects circular dependencies using a visiting set
80 /// - Recursively builds all dependencies before the component itself
81 /// - Invokes the builder function and stores the result in State
82 fn build_recursive(
83 &self,
84 type_id: &TypeId,
85 state: &State,
86 built: &mut HashSet<TypeId>,
87 visiting: &mut HashSet<TypeId>,
88 ) -> Result<(), DIError> {
89 if built.contains(type_id) {
90 return Ok(());
91 }
92
93 if visiting.contains(type_id) {
94 return Err(DIError::CircularDependency);
95 }
96
97 visiting.insert(*type_id);
98
99 // Explore to all the dependencies first
100 // and for each dependency, invoke build_recursive
101 // to ensure they are built before building the current type.
102
103 let dependency_graph = &self.components.get_dependency_graph();
104
105 if let Some(deps) = dependency_graph.read().get(type_id) {
106 for dep_id in deps {
107 self.build_recursive(dep_id, state, built, visiting)?;
108 }
109 }
110
111 visiting.remove(type_id);
112
113 if let Some(builder) = &self.components.get_builders().read().get(type_id) {
114 state.insert_instance(*type_id, builder(state)?);
115 built.insert(*type_id);
116 }
117
118 Ok(())
119 }
120}
121
122impl Default for DependencyContainer {
123 fn default() -> Self {
124 Self::new()
125 }
126}