Skip to main content

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}