Skip to main content

ferroui_splat/service_location/
dependency_resolver.rs

1//! `IReadonlyDependencyResolver`, `IMutableDependencyResolver` and `IDependencyResolver`, with the
2//! generic API over them.
3
4use super::service_type::{
5    erase_service, unerase_service, ErasedService, LazyDisposal, RegistrationCallback,
6    ServiceFactory, ServiceType,
7};
8use ferroui_system::IDisposable;
9use std::rc::Rc;
10
11/// Resolves services (`IReadonlyDependencyResolver`).
12///
13/// The methods are the object-safe core: callers use the generic forms of
14/// [`ReadonlyDependencyResolverExt`].
15// Deviation (DEVIATIONS.md, Splat): the `Type`-based overloads (`GetService(Type)`, ...) have no
16// counterpart; these erased methods are what the generic forms call, keyed by the `TypeId` of the
17// service type, and share the registrations of the generic forms.
18pub trait IReadonlyDependencyResolver {
19    /// The last service registered for `service_type` and `contract`, or `None`
20    /// (`GetService<T>(string? contract)`).
21    fn get_service_erased(
22        &self,
23        service_type: ServiceType,
24        contract: Option<&str>,
25    ) -> Option<ErasedService>;
26
27    /// Every service registered for `service_type` and `contract`, in the order of registration
28    /// (`GetServices<T>(string? contract)`).
29    fn get_services_erased(
30        &self,
31        service_type: ServiceType,
32        contract: Option<&str>,
33    ) -> Vec<ErasedService>;
34}
35
36/// Registers services (`IMutableDependencyResolver`).
37///
38/// The methods are the object-safe core: callers use the generic forms of
39/// [`MutableDependencyResolverExt`].
40pub trait IMutableDependencyResolver {
41    /// Whether a service is registered for `service_type` and `contract` (`HasRegistration<T>`).
42    fn has_registration_erased(&self, service_type: ServiceType, contract: Option<&str>) -> bool;
43
44    /// Registers a factory, called for each resolution (`Register<T>(Func<T?>, string?)`).
45    fn register_erased(
46        &self,
47        factory: ServiceFactory,
48        service_type: ServiceType,
49        contract: Option<&str>,
50    );
51
52    /// Registers a constant (`RegisterConstant<T>(T?, string?)`). `disposal` is what disposing the
53    /// resolver does with it: upstream disposes a constant that is `IDisposable`.
54    fn register_constant_erased(
55        &self,
56        value: Option<ErasedService>,
57        disposal: Option<Rc<dyn Fn()>>,
58        service_type: ServiceType,
59        contract: Option<&str>,
60    );
61
62    /// Registers a singleton created by `factory` on its first resolution
63    /// (`RegisterLazySingleton<T>(Func<T?>, string?)`). `disposal` is what disposing the resolver
64    /// does with the value once it was created.
65    fn register_lazy_singleton_erased(
66        &self,
67        factory: ServiceFactory,
68        disposal: Option<LazyDisposal>,
69        service_type: ServiceType,
70        contract: Option<&str>,
71    );
72
73    /// Removes the last registration (`UnregisterCurrent<T>(string?)`).
74    fn unregister_current_erased(&self, service_type: ServiceType, contract: Option<&str>);
75
76    /// Removes every registration (`UnregisterAll<T>(string?)`).
77    fn unregister_all_erased(&self, service_type: ServiceType, contract: Option<&str>);
78
79    /// Calls `callback` for every registration, those that exist and those to come, until the
80    /// returned disposable is disposed (`ServiceRegistrationCallback<T>(string?, Action<IDisposable>)`).
81    fn service_registration_callback_erased(
82        &self,
83        service_type: ServiceType,
84        contract: Option<&str>,
85        callback: RegistrationCallback,
86    ) -> Rc<dyn IDisposable>;
87}
88
89/// A resolver that both resolves and registers services, and is disposed
90/// (`IDependencyResolver`).
91pub trait IDependencyResolver: IReadonlyDependencyResolver + IMutableDependencyResolver + IDisposable {}
92
93impl<R: IReadonlyDependencyResolver + IMutableDependencyResolver + IDisposable + ?Sized>
94    IDependencyResolver for R
95{
96}
97
98/// The generic forms of [`IReadonlyDependencyResolver`].
99pub trait ReadonlyDependencyResolverExt: IReadonlyDependencyResolver {
100    /// `GetService<T>()`.
101    fn get_service<S: ?Sized + 'static>(&self) -> Option<Rc<S>> {
102        self.get_service_with_contract::<S>(None)
103    }
104
105    /// `GetService<T>(string? contract)`.
106    fn get_service_with_contract<S: ?Sized + 'static>(&self, contract: Option<&str>) -> Option<Rc<S>> {
107        self.get_service_erased(ServiceType::of::<S>(), contract)
108            .and_then(|service| unerase_service::<S>(&service))
109    }
110
111    /// `GetServices<T>()`.
112    fn get_services<S: ?Sized + 'static>(&self) -> Vec<Rc<S>> {
113        self.get_services_with_contract::<S>(None)
114    }
115
116    /// `GetServices<T>(string? contract)`.
117    fn get_services_with_contract<S: ?Sized + 'static>(&self, contract: Option<&str>) -> Vec<Rc<S>> {
118        self.get_services_erased(ServiceType::of::<S>(), contract)
119            .iter()
120            .filter_map(unerase_service::<S>)
121            .collect()
122    }
123}
124
125impl<R: IReadonlyDependencyResolver + ?Sized> ReadonlyDependencyResolverExt for R {}
126
127fn erase_factory<S: ?Sized + 'static>(factory: impl Fn() -> Option<Rc<S>> + 'static) -> ServiceFactory {
128    Rc::new(move || factory().map(erase_service))
129}
130
131/// The generic forms of [`IMutableDependencyResolver`].
132pub trait MutableDependencyResolverExt: IMutableDependencyResolver {
133    /// `HasRegistration<T>()`.
134    fn has_registration<S: ?Sized + 'static>(&self) -> bool {
135        self.has_registration_erased(ServiceType::of::<S>(), None)
136    }
137
138    /// `HasRegistration<T>(string? contract)`.
139    fn has_registration_with_contract<S: ?Sized + 'static>(&self, contract: Option<&str>) -> bool {
140        self.has_registration_erased(ServiceType::of::<S>(), contract)
141    }
142
143    /// `Register<T>(Func<T?>)`.
144    fn register<S: ?Sized + 'static>(&self, factory: impl Fn() -> Option<Rc<S>> + 'static) {
145        self.register_erased(erase_factory(factory), ServiceType::of::<S>(), None);
146    }
147
148    /// `Register<T>(Func<T?>, string? contract)`.
149    fn register_with_contract<S: ?Sized + 'static>(
150        &self,
151        factory: impl Fn() -> Option<Rc<S>> + 'static,
152        contract: Option<&str>,
153    ) {
154        self.register_erased(erase_factory(factory), ServiceType::of::<S>(), contract);
155    }
156
157    /// `RegisterConstant<T>(T)`.
158    // Deviation (DEVIATIONS.md, Splat): upstream disposes a constant that is `IDisposable` with the
159    // resolver; without reflection, `register_constant_disposable` is the form that does.
160    fn register_constant<S: ?Sized + 'static>(&self, value: Rc<S>) {
161        self.register_constant_erased(Some(erase_service(value)), None, ServiceType::of::<S>(), None);
162    }
163
164    /// `RegisterConstant<T>(T, string? contract)`.
165    fn register_constant_with_contract<S: ?Sized + 'static>(&self, value: Rc<S>, contract: Option<&str>) {
166        self.register_constant_erased(Some(erase_service(value)), None, ServiceType::of::<S>(), contract);
167    }
168
169    /// `RegisterConstant<T>(T)` of a constant that the resolver disposes when it is disposed.
170    fn register_constant_disposable<S: ?Sized + IDisposable + 'static>(&self, value: Rc<S>) {
171        self.register_constant_disposable_with_contract(value, None);
172    }
173
174    /// `RegisterConstant<T>(T, string? contract)` of a constant that the resolver disposes when it
175    /// is disposed.
176    fn register_constant_disposable_with_contract<S: ?Sized + IDisposable + 'static>(
177        &self,
178        value: Rc<S>,
179        contract: Option<&str>,
180    ) {
181        let disposed = value.clone();
182        self.register_constant_erased(
183            Some(erase_service(value)),
184            Some(Rc::new(move || disposed.dispose())),
185            ServiceType::of::<S>(),
186            contract,
187        );
188    }
189
190    /// `RegisterLazySingleton<T>(Func<T?>)`.
191    fn register_lazy_singleton<S: ?Sized + 'static>(&self, factory: impl Fn() -> Option<Rc<S>> + 'static) {
192        self.register_lazy_singleton_erased(erase_factory(factory), None, ServiceType::of::<S>(), None);
193    }
194
195    /// `RegisterLazySingleton<T>(Func<T?>, string? contract)`.
196    fn register_lazy_singleton_with_contract<S: ?Sized + 'static>(
197        &self,
198        factory: impl Fn() -> Option<Rc<S>> + 'static,
199        contract: Option<&str>,
200    ) {
201        self.register_lazy_singleton_erased(erase_factory(factory), None, ServiceType::of::<S>(), contract);
202    }
203
204    /// `RegisterLazySingleton<T>(Func<T?>)` of a singleton that the resolver disposes, once
205    /// created, when it is disposed.
206    fn register_lazy_singleton_disposable<S: ?Sized + IDisposable + 'static>(
207        &self,
208        factory: impl Fn() -> Option<Rc<S>> + 'static,
209    ) {
210        self.register_lazy_singleton_disposable_with_contract(factory, None);
211    }
212
213    /// `RegisterLazySingleton<T>(Func<T?>, string? contract)` of a singleton that the resolver
214    /// disposes, once created, when it is disposed.
215    fn register_lazy_singleton_disposable_with_contract<S: ?Sized + IDisposable + 'static>(
216        &self,
217        factory: impl Fn() -> Option<Rc<S>> + 'static,
218        contract: Option<&str>,
219    ) {
220        let disposal: LazyDisposal = Rc::new(|value: &ErasedService| {
221            if let Some(value) = unerase_service::<S>(value) {
222                value.dispose();
223            }
224        });
225        self.register_lazy_singleton_erased(
226            erase_factory(factory),
227            Some(disposal),
228            ServiceType::of::<S>(),
229            contract,
230        );
231    }
232
233    /// `UnregisterCurrent<T>()`.
234    fn unregister_current<S: ?Sized + 'static>(&self) {
235        self.unregister_current_erased(ServiceType::of::<S>(), None);
236    }
237
238    /// `UnregisterCurrent<T>(string? contract)`.
239    fn unregister_current_with_contract<S: ?Sized + 'static>(&self, contract: Option<&str>) {
240        self.unregister_current_erased(ServiceType::of::<S>(), contract);
241    }
242
243    /// `UnregisterAll<T>()`.
244    fn unregister_all<S: ?Sized + 'static>(&self) {
245        self.unregister_all_erased(ServiceType::of::<S>(), None);
246    }
247
248    /// `UnregisterAll<T>(string? contract)`.
249    fn unregister_all_with_contract<S: ?Sized + 'static>(&self, contract: Option<&str>) {
250        self.unregister_all_erased(ServiceType::of::<S>(), contract);
251    }
252
253    /// `ServiceRegistrationCallback<T>(Action<IDisposable>)`.
254    fn service_registration_callback<S: ?Sized + 'static>(
255        &self,
256        callback: impl Fn(&dyn IDisposable) + 'static,
257    ) -> Rc<dyn IDisposable> {
258        self.service_registration_callback_erased(ServiceType::of::<S>(), None, Rc::new(callback))
259    }
260
261    /// `ServiceRegistrationCallback<T>(string? contract, Action<IDisposable>)`.
262    fn service_registration_callback_with_contract<S: ?Sized + 'static>(
263        &self,
264        contract: Option<&str>,
265        callback: impl Fn(&dyn IDisposable) + 'static,
266    ) -> Rc<dyn IDisposable> {
267        self.service_registration_callback_erased(ServiceType::of::<S>(), contract, Rc::new(callback))
268    }
269}
270
271impl<R: IMutableDependencyResolver + ?Sized> MutableDependencyResolverExt for R {}