Skip to main content

nestforge_core/
container.rs

1use std::{
2    any::{Any, TypeId},
3    collections::HashMap,
4    sync::{Arc, RwLock},
5};
6
7use anyhow::{anyhow, Result};
8
9/**
10* Container = our tiny dependency injection store (v1).
11* 
12* What it does:
13* - stores values by type (TypeId).
14* - lets us register services/config once.
15* - lets us resolve them later.
16* 
17* Why Arc?
18* - so multiple parts of the app can share the same service safely.
19* 
20* Why RwLock?
21* - allows safe reads/writes across threads.
22* - write when registering.
23* - read when resolving.
24*/
25
26#[derive(Clone, Default)]
27pub struct Container {
28    inner: Arc<RwLock<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>>,
29}
30
31impl Container {
32    /**
33    * Nice helper constructor.
34    * Same as Default, just cleaner to read in app code.
35    */
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /**
41    * Register a value/service into the container.
42    * 
43    * Example:
44    * container.register(AppConfig { ... })?;
45    * 
46    * Rules:
47    * - T must be thread-safe (Send + Sync).
48    * - T must be 'static because we store it for the app lifetime.
49    */
50    pub fn register<T>(&self, value: T) -> Result<()>
51    where
52        T: Send + Sync + 'static,
53    {
54        let mut map = self
55            .inner
56            .write()
57            .map_err(|_| anyhow!("Container write lock poisoned"))?;
58
59        let type_id = TypeId::of::<T>();
60
61        /* Prevent accidental duplicate registration of the same type. */
62        if map.contains_key(&type_id) {
63            return Err(anyhow!(
64                "Type already registered: {}",
65                std::any::type_name::<T>()
66            ));
67        }
68
69         /* Store as Arc<dyn Any> so we can keep different types in one map. */
70        map.insert(type_id, Arc::new(value));
71        Ok(())
72    }
73
74    /**
75    * Resolve (get back) a registered value/service by type.
76    * 
77    * Example:
78    * let config = container.resolve::<AppConfig>()?;
79    * 
80    * Returns Arc<T> so the caller can clone/share it cheaply.
81    */
82    pub fn resolve<T>(&self) -> Result<Arc<T>>
83    where
84        T: Send + Sync + 'static,
85    {
86        let map = self
87            .inner
88            .read()
89            .map_err(|_| anyhow!("Container read lock poisoned"))?;
90
91        let value = map
92            .get(&TypeId::of::<T>())
93            .ok_or_else(|| anyhow!("Type not registered: {}", std::any::type_name::<T>()))?
94            .clone();
95
96        /*
97        * We stored the value as dyn Any, so now we downcast it back to the real type T.
98        * If downcast fails, the type in the map doesn’t match what we asked for.
99        */
100        value.downcast::<T>()
101            .map_err(|_| anyhow!("Failed to downcast: {}", std::any::type_name::<T>()))
102    }
103}