Skip to main content

churust_core/
state.rs

1//! Type-keyed shared application state (a minimal DI registry).
2
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::sync::Arc;
6
7/// A type-keyed registry holding at most one shared value per type — a minimal
8/// dependency-injection container.
9///
10/// Each value is keyed by its [`TypeId`] and stored behind an [`Arc`], so reads
11/// hand out cheap shared handles. This backs [`AppBuilder::state`] and the
12/// [`State`](crate::State) extractor; you rarely construct one directly.
13///
14/// ```
15/// use churust_core::StateMap;
16///
17/// let mut state = StateMap::new();
18/// state.insert(42u32);
19/// state.insert(String::from("hi"));
20/// assert_eq!(*state.get::<u32>().unwrap(), 42);
21/// assert_eq!(state.get::<String>().unwrap().as_str(), "hi");
22/// assert!(state.get::<bool>().is_none());
23/// ```
24///
25/// [`AppBuilder::state`]: crate::AppBuilder::state
26#[derive(Clone, Default)]
27pub struct StateMap {
28    map: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
29}
30
31impl StateMap {
32    /// Create an empty registry. Equivalent to [`StateMap::default`].
33    ///
34    /// ```
35    /// use churust_core::StateMap;
36    /// assert!(StateMap::new().get::<u32>().is_none());
37    /// ```
38    pub fn new() -> Self {
39        Self::default()
40    }
41
42    /// Insert (or replace) the single value held for type `T`.
43    ///
44    /// ```
45    /// use churust_core::StateMap;
46    /// let mut state = StateMap::new();
47    /// state.insert(1u8);
48    /// state.insert(2u8); // replaces the previous u8
49    /// assert_eq!(*state.get::<u8>().unwrap(), 2);
50    /// ```
51    pub fn insert<T: Send + Sync + 'static>(&mut self, value: T) {
52        self.map.insert(TypeId::of::<T>(), Arc::new(value));
53    }
54
55    /// Get a shared [`Arc`] handle to the value for type `T`, or `None` if no
56    /// value of that type has been inserted.
57    pub fn get<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
58        self.map
59            .get(&TypeId::of::<T>())
60            .and_then(|arc| arc.clone().downcast::<T>().ok())
61    }
62}
63
64impl std::fmt::Debug for StateMap {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        f.debug_struct("StateMap")
67            .field("len", &self.map.len())
68            .finish()
69    }
70}
71
72#[cfg(test)]
73mod tests {
74    use super::*;
75
76    #[test]
77    fn stores_and_retrieves_by_type() {
78        let mut m = StateMap::new();
79        m.insert(7u32);
80        m.insert(String::from("hello"));
81        assert_eq!(*m.get::<u32>().unwrap(), 7);
82        assert_eq!(m.get::<String>().unwrap().as_str(), "hello");
83        assert!(m.get::<i64>().is_none());
84    }
85}