Skip to main content

sword_core/injectables/
components.rs

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