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}