Skip to main content

modo/service/
registry.rs

1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::sync::Arc;
4
5/// A type-map used to register services before the application starts.
6///
7/// `Registry` is the mutable builder counterpart to [`AppState`](super::AppState).
8/// Add all services during startup, then call [`into_state`](Registry::into_state) to
9/// produce the immutable [`AppState`](super::AppState) that axum holds.
10///
11/// Each type `T` can be registered at most once; a second call to [`add`](Registry::add)
12/// with the same type overwrites the previous entry.
13///
14/// # Example
15///
16/// ```
17/// use modo::service::Registry;
18///
19/// # struct MyService;
20/// # impl MyService { fn new() -> Self { Self } }
21/// let mut registry = Registry::new();
22/// registry.add(MyService::new());
23/// let state = registry.into_state();
24/// ```
25pub struct Registry {
26    services: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
27}
28
29impl Registry {
30    /// Creates an empty registry.
31    pub fn new() -> Self {
32        Self {
33            services: HashMap::new(),
34        }
35    }
36
37    /// Registers `service` under its concrete type `T`.
38    ///
39    /// If a service of type `T` was already registered, it is replaced.
40    pub fn add<T: Send + Sync + 'static>(&mut self, service: T) {
41        self.services.insert(TypeId::of::<T>(), Arc::new(service));
42    }
43
44    /// Returns a reference-counted handle to the service registered as type `T`,
45    /// or `None` if no such service exists.
46    ///
47    /// Useful for startup validation — to confirm a required service was registered
48    /// before the server begins accepting requests.
49    pub fn get<T: Send + Sync + 'static>(&self) -> Option<Arc<T>> {
50        self.services
51            .get(&TypeId::of::<T>())
52            .and_then(|arc| arc.clone().downcast::<T>().ok())
53    }
54
55    /// Returns a point-in-time snapshot of the registry for internal use.
56    pub(crate) fn snapshot(&self) -> Arc<super::RegistrySnapshot> {
57        Arc::new(super::RegistrySnapshot::new(self.services.clone()))
58    }
59
60    /// Consumes the registry and returns an [`AppState`](super::AppState) suitable for
61    /// passing to [`Router::with_state`](axum::Router::with_state).
62    pub fn into_state(self) -> super::AppState {
63        super::AppState::from(self)
64    }
65
66    pub(crate) fn into_inner(self) -> HashMap<TypeId, Arc<dyn Any + Send + Sync>> {
67        self.services
68    }
69}
70
71impl Default for Registry {
72    fn default() -> Self {
73        Self::new()
74    }
75}