Skip to main content

sword_core/injectables/
components.rs

1use crate::{DependencyInjectionError, HasDeps, Injectable, RwMap, State};
2
3use std::{
4    any::{TypeId, type_name},
5    collections::HashMap,
6    sync::Arc,
7};
8
9type ComponentBuilderFn =
10    Box<dyn Fn(&State) -> Result<Injectable, DependencyInjectionError>>;
11
12/// Trait for injectable components that can be automatically constructed
13/// by the dependency container with automatic dependency resolution.
14///
15/// Components are services or dependencies that need to be built from other
16/// components in the State.
17///
18/// Use the `#[injectable]` macro to automatically implement this trait.
19pub trait Component: HasDeps {}
20
21pub struct ComponentRegistry {
22    builders: RwMap<TypeId, ComponentBuilderFn>,
23    dependency_graph: RwMap<TypeId, Vec<TypeId>>,
24}
25
26impl ComponentRegistry {
27    pub(crate) fn new() -> Self {
28        Self {
29            builders: RwMap::new(HashMap::new()),
30            dependency_graph: RwMap::new(HashMap::new()),
31        }
32    }
33
34    pub fn register<T: Component>(&self) {
35        let type_id = TypeId::of::<T>();
36        let type_name = type_name::<T>();
37
38        let component_builder = Box::new(move |state: &State| {
39            T::build(state)
40                .map(|instance| Arc::new(instance) as Injectable)
41                .map_err(|e| DependencyInjectionError::BuildFailed {
42                    type_name: type_name.to_string(),
43                    reason: e.to_string(),
44                })
45        });
46
47        self.dependency_graph.write().insert(type_id, T::deps());
48        self.builders.write().insert(type_id, component_builder);
49    }
50
51    pub(crate) fn get_builders(&self) -> &RwMap<TypeId, ComponentBuilderFn> {
52        &self.builders
53    }
54
55    pub(crate) fn get_dependency_graph(&self) -> &RwMap<TypeId, Vec<TypeId>> {
56        &self.dependency_graph
57    }
58}