Skip to main content

ferroui_dependency_injection/
activator_utilities.rs

1//! `ActivatorUtilities.cs`, `ObjectFactory.cs`, `ObjectFactoryT.cs`,
2//! `FromKeyedServicesAttribute.cs` and `ServiceKeyAttribute.cs`.
3//! Reflection is replaced by typed constructor factories and explicit dependency metadata.
4
5use crate::*;
6use std::rc::Rc;
7
8type Constructor<S> =
9    Rc<dyn Fn(&dyn IServiceProvider, Option<&ServiceKey>) -> ServiceResult<Rc<S>>>;
10pub struct ObjectFactory<S: ?Sized> {
11    pub(crate) factory: Constructor<S>,
12    pub(crate) dependencies: Vec<Dependency>,
13}
14impl<S: ?Sized> Clone for ObjectFactory<S> {
15    fn clone(&self) -> Self {
16        Self {
17            factory: self.factory.clone(),
18            dependencies: self.dependencies.clone(),
19        }
20    }
21}
22impl<S: ?Sized + 'static> ObjectFactory<S> {
23    pub fn new(
24        dependencies: Vec<Dependency>,
25        factory: impl Fn(&dyn IServiceProvider, Option<&ServiceKey>) -> ServiceResult<Rc<S>> + 'static,
26    ) -> Self {
27        Self {
28            dependencies,
29            factory: Rc::new(factory),
30        }
31    }
32}
33
34/// Creates unregistered instances from the same factories as registrations. Such instances
35/// belong to the caller, not to a provider's disposal list. Explicit constructor arguments can
36/// be captured by the factory; Rust checks their types and the selected constructor.
37pub struct ActivatorUtilities;
38impl ActivatorUtilities {
39    pub fn create_factory<S: ?Sized + 'static>(factory: ObjectFactory<S>) -> ObjectFactory<S> {
40        factory
41    }
42    pub fn create_instance<S: ?Sized + 'static>(
43        provider: &dyn IServiceProvider,
44        factory: &ObjectFactory<S>,
45    ) -> ServiceResult<Rc<S>> {
46        Self::create_keyed_instance(provider, None, factory)
47    }
48    pub fn create_keyed_instance<S: ?Sized + 'static>(
49        provider: &dyn IServiceProvider,
50        key: Option<&ServiceKey>,
51        factory: &ObjectFactory<S>,
52    ) -> ServiceResult<Rc<S>> {
53        provider.ensure_active()?;
54        (factory.factory)(provider, key)
55    }
56    pub fn get_service_or_create_instance<S: ?Sized + 'static>(
57        provider: &dyn IServiceProvider,
58        factory: &ObjectFactory<S>,
59    ) -> ServiceResult<Rc<S>> {
60        match provider.get_service::<S>()? {
61            Some(value) => Ok(value),
62            None => Self::create_instance(provider, factory),
63        }
64    }
65}
66
67/// Writes a factory and its validation metadata from typed constructor parameters.
68///
69/// Parameters use `required(T)`, `optional(T)`, `all(T)`, `keyed(T, key)`,
70/// `inherited(T)`, `key()` (the current `Option<ServiceKey>`), or `typed_key(T)`
71/// (a cloned key of type `T`, checked at activation).
72/// The body returns `Rc<S>`; use `ObjectFactory::new` for a fallible constructor.
73/// ```
74/// # use ferroui_dependency_injection::*;
75/// # use std::rc::Rc;
76/// let factory = service_factory!(String, |name: required(str)| Rc::new(name.to_string()));
77/// let descriptor = ServiceDescriptor::from_factory(ServiceLifetime::Transient, factory);
78/// ```
79#[macro_export]
80macro_rules! service_factory {
81    ($service:ty, || $body:expr) => { $crate::ObjectFactory::<$service>::new(Vec::new(), move |_, _| Ok($body)) };
82    ($service:ty, |$($name:ident : $kind:ident($($args:tt)*)),* $(,)?| $body:expr) => {{
83        let dependencies: Vec<Option<$crate::Dependency>> = vec![$($crate::service_factory!(@dependency $kind($($args)*))),*];
84        $crate::ObjectFactory::<$service>::new(dependencies.iter().flatten().cloned().collect(), move |provider, service_key| {
85            let mut parameters = dependencies.iter();
86            $(let dependency = parameters.next().expect("constructor parameter");
87              let $name = $crate::service_factory!(@resolve provider, service_key, dependency, $kind($($args)*));)*
88            let _ = &mut parameters;
89            let _ = (&provider, &service_key);
90            Ok($body)
91        })
92    }};
93    (@dependency required($t:ty)) => { Some($crate::Dependency::required::<$t>()) };
94    (@dependency optional($t:ty)) => { Some($crate::Dependency::required::<$t>().optional()) };
95    (@dependency all($t:ty)) => { Some($crate::Dependency::required::<$t>().enumerable()) };
96    (@dependency keyed($t:ty, $key:expr)) => { Some($crate::Dependency::keyed::<$t>(Some(($key).into()))) };
97    (@dependency inherited($t:ty)) => { Some($crate::Dependency::inherited::<$t>()) };
98    (@dependency typed_key($t:ty)) => { None::<$crate::Dependency> };
99    (@dependency key()) => { None::<$crate::Dependency> };
100    (@resolve $p:ident, $k:ident, $d:ident, required($t:ty)) => {{ let _ = $d; $crate::ServiceProviderServiceExtensions::get_required_service::<$t>($p)? }};
101    (@resolve $p:ident, $k:ident, $d:ident, optional($t:ty)) => {{ let _ = $d; $crate::ServiceProviderServiceExtensions::get_service::<$t>($p)? }};
102    (@resolve $p:ident, $k:ident, $d:ident, all($t:ty)) => {{ let _ = $d; $crate::ServiceProviderServiceExtensions::get_services::<$t>($p)? }};
103    (@resolve $p:ident, $k:ident, $d:ident, keyed($t:ty, $key:expr)) => {{ let _ = $d; $crate::ServiceProviderServiceExtensions::get_required_keyed_service::<$t>($p, $d.as_ref().expect("keyed parameter").identifier.service_key.as_ref())? }};
104    (@resolve $p:ident, $k:ident, $d:ident, inherited($t:ty)) => {{ let _ = $d; $crate::ServiceProviderServiceExtensions::get_required_keyed_service::<$t>($p, $k)? }};
105    (@resolve $p:ident, $k:ident, $d:ident, typed_key($t:ty)) => {{
106        let _ = $d;
107        $k.and_then(|key| key.downcast_ref::<$t>()).cloned()
108            .ok_or($crate::ServiceError::TypeMismatch($crate::ServiceType::of::<$t>()))?
109    }};
110    (@resolve $p:ident, $k:ident, $d:ident, key()) => {{ let _ = $d; $k.cloned() }};
111}