1use std::{
2 any::{Any, TypeId},
3 collections::HashMap,
4 sync::Arc,
5};
6use thiserror::Error;
7
8#[derive(Clone)]
11pub struct Shared<T: ?Sized + Send + Sync + 'static> {
12 inner: Arc<T>,
13}
14
15impl<T: ?Sized + Send + Sync + 'static> Shared<T> {
16 pub const fn new(inner: Arc<T>) -> Self {
17 Self { inner }
18 }
19
20 pub fn inner(&self) -> Arc<T> {
21 Arc::clone(&self.inner)
22 }
23}
24
25impl<T: ?Sized + Send + Sync + 'static> std::fmt::Debug for Shared<T> {
26 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 formatter
28 .debug_struct("Shared")
29 .field("service", &std::any::type_name::<T>())
30 .finish_non_exhaustive()
31 }
32}
33
34#[derive(Default)]
35pub struct ServiceCollection {
36 services: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
37}
38
39impl std::fmt::Debug for ServiceCollection {
40 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 formatter
42 .debug_struct("ServiceCollection")
43 .field("service_count", &self.services.len())
44 .finish()
45 }
46}
47
48impl ServiceCollection {
49 pub fn insert<T>(&mut self, value: Arc<T>) -> Result<(), ServiceError>
50 where
51 T: Any + Send + Sync,
52 {
53 let type_id = TypeId::of::<T>();
54 if self.services.contains_key(&type_id) {
55 return Err(ServiceError::Duplicate(std::any::type_name::<T>()));
56 }
57 self.services.insert(type_id, value);
58 Ok(())
59 }
60
61 pub fn insert_shared<T>(&mut self, value: Arc<T>) -> Result<(), ServiceError>
62 where
63 T: ?Sized + Send + Sync + 'static,
64 {
65 self.insert(Arc::new(Shared::new(value)))
66 }
67
68 pub fn contains<T>(&self) -> bool
69 where
70 T: Any + Send + Sync,
71 {
72 self.services.contains_key(&TypeId::of::<T>())
73 }
74
75 pub fn contains_shared<T>(&self) -> bool
76 where
77 T: ?Sized + Send + Sync + 'static,
78 {
79 self.contains::<Shared<T>>()
80 }
81
82 pub fn get<T>(&self) -> Result<Arc<T>, ServiceError>
83 where
84 T: Any + Send + Sync,
85 {
86 get_service(&self.services)
87 }
88
89 pub fn get_optional<T>(&self) -> Result<Option<Arc<T>>, ServiceError>
90 where
91 T: Any + Send + Sync,
92 {
93 get_optional_service(&self.services)
94 }
95
96 pub fn get_shared<T>(&self) -> Result<Arc<T>, ServiceError>
97 where
98 T: ?Sized + Send + Sync + 'static,
99 {
100 self.get::<Shared<T>>().map(|service| service.inner())
101 }
102
103 pub fn get_optional_shared<T>(&self) -> Result<Option<Arc<T>>, ServiceError>
104 where
105 T: ?Sized + Send + Sync + 'static,
106 {
107 self.get_optional::<Shared<T>>()
108 .map(|service| service.map(|value| value.inner()))
109 }
110
111 pub fn freeze(self) -> FrozenServices {
112 FrozenServices {
113 services: self.services,
114 }
115 }
116}
117
118#[derive(Clone, Default)]
119pub struct FrozenServices {
120 services: HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
121}
122
123impl std::fmt::Debug for FrozenServices {
124 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
125 formatter
126 .debug_struct("FrozenServices")
127 .field("service_count", &self.services.len())
128 .finish()
129 }
130}
131
132impl FrozenServices {
133 pub fn get<T>(&self) -> Result<Arc<T>, ServiceError>
134 where
135 T: Any + Send + Sync,
136 {
137 get_service(&self.services)
138 }
139
140 pub fn get_optional<T>(&self) -> Result<Option<Arc<T>>, ServiceError>
141 where
142 T: Any + Send + Sync,
143 {
144 get_optional_service(&self.services)
145 }
146
147 pub fn get_shared<T>(&self) -> Result<Arc<T>, ServiceError>
148 where
149 T: ?Sized + Send + Sync + 'static,
150 {
151 self.get::<Shared<T>>().map(|service| service.inner())
152 }
153
154 pub fn get_optional_shared<T>(&self) -> Result<Option<Arc<T>>, ServiceError>
155 where
156 T: ?Sized + Send + Sync + 'static,
157 {
158 self.get_optional::<Shared<T>>()
159 .map(|service| service.map(|value| value.inner()))
160 }
161}
162
163fn get_service<T>(
164 services: &HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
165) -> Result<Arc<T>, ServiceError>
166where
167 T: Any + Send + Sync,
168{
169 services
170 .get(&TypeId::of::<T>())
171 .cloned()
172 .ok_or_else(|| ServiceError::Missing(std::any::type_name::<T>()))?
173 .downcast::<T>()
174 .map_err(|_| ServiceError::TypeMismatch(std::any::type_name::<T>()))
175}
176
177fn get_optional_service<T>(
178 services: &HashMap<TypeId, Arc<dyn Any + Send + Sync>>,
179) -> Result<Option<Arc<T>>, ServiceError>
180where
181 T: Any + Send + Sync,
182{
183 services
184 .get(&TypeId::of::<T>())
185 .cloned()
186 .map(|value| {
187 value
188 .downcast::<T>()
189 .map_err(|_| ServiceError::TypeMismatch(std::any::type_name::<T>()))
190 })
191 .transpose()
192}
193
194#[derive(Debug, Error, Clone, PartialEq, Eq)]
195pub enum ServiceError {
196 #[error("service is already registered: {0}")]
197 Duplicate(&'static str),
198 #[error("service is not registered: {0}")]
199 Missing(&'static str),
200 #[error("service has an unexpected concrete type: {0}")]
201 TypeMismatch(&'static str),
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207
208 trait Greeting: Send + Sync {
209 fn text(&self) -> &'static str;
210 }
211
212 #[derive(Debug)]
213 struct Hello;
214 impl Greeting for Hello {
215 fn text(&self) -> &'static str {
216 "hello"
217 }
218 }
219
220 #[test]
221 fn typed_services_are_frozen_and_retrieved_without_string_keys() {
222 let mut services = ServiceCollection::default();
223 services.insert(Arc::new(String::from("hello"))).unwrap();
224 let frozen = services.freeze();
225 assert_eq!(&*frozen.get::<String>().unwrap(), "hello");
226 }
227
228 #[test]
229 fn trait_object_services_are_registered_without_double_arc_call_sites() {
230 let mut services = ServiceCollection::default();
231 services
232 .insert_shared::<dyn Greeting>(Arc::new(Hello))
233 .unwrap();
234 assert_eq!(
235 services.get_shared::<dyn Greeting>().unwrap().text(),
236 "hello"
237 );
238 }
239
240 #[test]
241 fn optional_service_lookup_distinguishes_absence_from_type_errors() {
242 let services = ServiceCollection::default();
243 assert!(services.get_optional::<String>().unwrap().is_none());
244 assert!(
245 services
246 .get_optional_shared::<dyn Greeting>()
247 .unwrap()
248 .is_none()
249 );
250
251 let frozen = services.freeze();
252 assert!(frozen.get_optional::<String>().unwrap().is_none());
253 assert!(
254 frozen
255 .get_optional_shared::<dyn Greeting>()
256 .unwrap()
257 .is_none()
258 );
259 }
260
261 #[test]
262 fn duplicate_service_types_are_rejected() {
263 let mut services = ServiceCollection::default();
264 services.insert(Arc::new(1_u64)).unwrap();
265 assert!(matches!(
266 services.insert(Arc::new(2_u64)),
267 Err(ServiceError::Duplicate(_))
268 ));
269 }
270}