rust_web_server/di/mod.rs
1//! Lightweight type-keyed dependency injection container.
2//!
3//! [`Container`] stores services keyed by their Rust type (`TypeId`). Register
4//! services at startup; resolve them anywhere that has access to the container.
5//! Share across request handlers by wrapping in `Arc<Container>` and using
6//! `App::with_state(container.into_arc())`.
7//!
8//! # Concrete services
9//!
10//! ```rust
11//! use rust_web_server::di::Container;
12//!
13//! struct EmailService { host: String }
14//!
15//! let mut c = Container::new();
16//! c.register(EmailService { host: "smtp.example.com".into() });
17//!
18//! let svc = c.get::<EmailService>().unwrap();
19//! assert_eq!(svc.host, "smtp.example.com");
20//! ```
21//!
22//! # Trait-object services
23//!
24//! ```rust
25//! use std::sync::Arc;
26//! use rust_web_server::di::Container;
27//!
28//! pub trait Mailer: Send + Sync {
29//! fn send(&self, to: &str);
30//! }
31//! struct SmtpMailer;
32//! impl Mailer for SmtpMailer {
33//! fn send(&self, _to: &str) {}
34//! }
35//!
36//! let mut c = Container::new();
37//! c.provide::<dyn Mailer>(Arc::new(SmtpMailer));
38//! let mailer = c.get::<dyn Mailer>().unwrap();
39//! mailer.send("user@example.com");
40//! ```
41//!
42//! # Named services
43//!
44//! Register multiple instances of the same type under distinct string names:
45//!
46//! ```rust
47//! use rust_web_server::di::Container;
48//!
49//! let mut c = Container::new();
50//! c.register_named("primary", 5432u16)
51//! .register_named("replica", 5433u16);
52//!
53//! assert_eq!(*c.get_named::<u16>("primary").unwrap(), 5432);
54//! assert_eq!(*c.get_named::<u16>("replica").unwrap(), 5433);
55//! ```
56//!
57//! # Usage with `App::with_state`
58//!
59//! ```rust,no_run
60//! use std::sync::Arc;
61//! use rust_web_server::app::App;
62//! use rust_web_server::di::Container;
63//! use rust_web_server::request::Request;
64//! use rust_web_server::router::PathParams;
65//! use rust_web_server::server::ConnectionInfo;
66//! use rust_web_server::response::{Response, STATUS_CODE_REASON_PHRASE};
67//! use rust_web_server::routes;
68//!
69//! struct Config { version: &'static str }
70//!
71//! fn get_version(
72//! _req: &Request,
73//! _p: &PathParams,
74//! _c: &ConnectionInfo,
75//! state: &Arc<Container>,
76//! ) -> Response {
77//! let _cfg = state.get::<Config>().unwrap();
78//! Response::get_response(&STATUS_CODE_REASON_PHRASE.n200_ok, None, None)
79//! }
80//!
81//! let mut container = Container::new();
82//! container.register(Config { version: "1.0" });
83//!
84//! let app = routes! {
85//! App::with_state(container.into_arc()),
86//! GET "/version" => get_version,
87//! };
88//! ```
89
90use std::any::{Any, TypeId};
91use std::collections::HashMap;
92use std::sync::Arc;
93
94/// A type-keyed service container for dependency injection.
95///
96/// See the [module documentation][self] for usage examples.
97pub struct Container {
98 /// Unnamed services: key = TypeId of T, value = Box<dyn Any> holding Arc<T>.
99 services: HashMap<TypeId, Box<dyn Any + Send + Sync>>,
100 /// Named services: key = (TypeId of T, name), value = Box<dyn Any> holding Arc<T>.
101 named: HashMap<(TypeId, String), Box<dyn Any + Send + Sync>>,
102}
103
104impl Default for Container {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110impl Container {
111 /// Create an empty container.
112 pub fn new() -> Self {
113 Container {
114 services: HashMap::new(),
115 named: HashMap::new(),
116 }
117 }
118
119 // ── Registration ────────────────────────────────────────────────────────────
120
121 /// Register a concrete service by value.
122 ///
123 /// Wraps `service` in `Arc<T>` and stores it under `TypeId::of::<T>()`.
124 /// A subsequent registration for the same `T` replaces the previous one.
125 pub fn register<T: Send + Sync + 'static>(&mut self, service: T) -> &mut Self {
126 self.services
127 .insert(TypeId::of::<T>(), Box::new(Arc::new(service)));
128 self
129 }
130
131 /// Register a pre-built `Arc<T>`.
132 ///
133 /// Required when registering **trait objects**, because the concrete type
134 /// must be erased before storage:
135 ///
136 /// ```rust
137 /// # use std::sync::Arc;
138 /// # use rust_web_server::di::Container;
139 /// # trait Greeter: Send + Sync { fn greet(&self) -> &str; }
140 /// # struct Hello; impl Greeter for Hello { fn greet(&self) -> &str { "hi" } }
141 /// let mut c = Container::new();
142 /// c.provide::<dyn Greeter>(Arc::new(Hello));
143 /// assert_eq!(c.get::<dyn Greeter>().unwrap().greet(), "hi");
144 /// ```
145 ///
146 /// Also usable for concrete types when you already have an `Arc`.
147 pub fn provide<T>(&mut self, service: Arc<T>) -> &mut Self
148 where
149 T: ?Sized + Send + Sync + 'static,
150 {
151 // Arc<T> is always Sized (fat pointer for DSTs), 'static when T: 'static,
152 // Send + Sync when T: Send + Sync, and Any via blanket impl for all 'static.
153 let boxed: Box<dyn Any + Send + Sync> = Box::new(service);
154 self.services.insert(TypeId::of::<T>(), boxed);
155 self
156 }
157
158 /// Register a named concrete service.
159 ///
160 /// Use this when you need multiple instances of the same type
161 /// (e.g., primary vs. replica database pools).
162 pub fn register_named<T: Send + Sync + 'static>(
163 &mut self,
164 name: impl Into<String>,
165 service: T,
166 ) -> &mut Self {
167 self.named.insert(
168 (TypeId::of::<T>(), name.into()),
169 Box::new(Arc::new(service)),
170 );
171 self
172 }
173
174 /// Register a pre-built `Arc<T>` under a string name.
175 ///
176 /// Use for named trait-object registrations.
177 pub fn provide_named<T>(
178 &mut self,
179 name: impl Into<String>,
180 service: Arc<T>,
181 ) -> &mut Self
182 where
183 T: ?Sized + Send + Sync + 'static,
184 {
185 let boxed: Box<dyn Any + Send + Sync> = Box::new(service);
186 self.named.insert((TypeId::of::<T>(), name.into()), boxed);
187 self
188 }
189
190 // ── Resolution ──────────────────────────────────────────────────────────────
191
192 /// Resolve a service by type.
193 ///
194 /// Works for both **concrete types** (`get::<MyService>()`) and **trait
195 /// objects** (`get::<dyn MyTrait>()`). Returns `None` if no service of
196 /// that type has been registered.
197 pub fn get<T>(&self) -> Option<Arc<T>>
198 where
199 T: ?Sized + 'static,
200 Arc<T>: Clone,
201 {
202 self.services
203 .get(&TypeId::of::<T>())
204 .and_then(|b| b.downcast_ref::<Arc<T>>())
205 .cloned()
206 }
207
208 /// Resolve a named service by type and name.
209 ///
210 /// Returns `None` if no service of that type and name has been registered.
211 pub fn get_named<T>(&self, name: &str) -> Option<Arc<T>>
212 where
213 T: ?Sized + 'static,
214 Arc<T>: Clone,
215 {
216 self.named
217 .get(&(TypeId::of::<T>(), name.to_string()))
218 .and_then(|b| b.downcast_ref::<Arc<T>>())
219 .cloned()
220 }
221
222 // ── Inspection ──────────────────────────────────────────────────────────────
223
224 /// Returns `true` if a service of type `T` has been registered (unnamed).
225 pub fn contains<T: ?Sized + 'static>(&self) -> bool {
226 self.services.contains_key(&TypeId::of::<T>())
227 }
228
229 /// Returns `true` if a named service of type `T` with `name` exists.
230 pub fn contains_named<T: ?Sized + 'static>(&self, name: &str) -> bool {
231 self.named
232 .contains_key(&(TypeId::of::<T>(), name.to_string()))
233 }
234
235 /// Total number of unnamed registrations.
236 pub fn len(&self) -> usize {
237 self.services.len()
238 }
239
240 /// Returns `true` if no services have been registered.
241 pub fn is_empty(&self) -> bool {
242 self.services.is_empty()
243 }
244
245 // ── Sharing ─────────────────────────────────────────────────────────────────
246
247 /// Seal this container into an `Arc<Container>` for sharing across threads
248 /// and handler closures.
249 ///
250 /// Typical usage with `App::with_state`:
251 /// ```rust,no_run
252 /// # use rust_web_server::di::Container;
253 /// # use rust_web_server::app::App;
254 /// # use rust_web_server::routes;
255 /// let mut c = Container::new();
256 /// // c.register(...);
257 /// let app = routes! { App::with_state(c.into_arc()), };
258 /// ```
259 pub fn into_arc(self) -> Arc<Self> {
260 Arc::new(self)
261 }
262}
263
264#[cfg(test)]
265mod tests;