ferroui_base/metadata/service_provider.rs
1//! The service provider contract used by markup: the equivalent of the
2//! runtime library's `IServiceProvider`.
3
4use std::any::{Any, TypeId};
5use std::rc::Rc;
6
7/// Retrieves service objects by the type of their handle.
8///
9/// A service is identified by the Rust type of the handle it is used
10/// through, normally the handle of a contract (`Rc<dyn IProvideValueTarget>`,
11/// `Rc<dyn IRootObjectProvider>`, `Rc<dyn INameScope>`). The provider returns
12/// the handle boxed as [`Any`]; the box holds exactly a value of the
13/// requested type.
14///
15/// ```ignore
16/// let target: Option<Rc<dyn IProvideValueTarget>> = service_provider.get_service_of();
17/// ```
18pub trait IServiceProvider {
19 /// Gets the service whose handle type is `service_type`, or `None` if
20 /// the provider has no such service. The returned box holds a value of
21 /// exactly the type identified by `service_type`.
22 fn get_service(&self, service_type: TypeId) -> Option<Rc<dyn Any>>;
23}
24
25impl<'a> dyn IServiceProvider + 'a {
26 /// Gets the service used through the handle type `T`
27 /// (`GetService(typeof(T))` followed by the cast to `T`).
28 pub fn get_service_of<T: Clone + 'static>(&self) -> Option<T> {
29 self.get_service(TypeId::of::<T>())?.downcast_ref::<T>().cloned()
30 }
31}
32
33/// Service provider handles compare by identity, so that they can be held
34/// in untyped values.
35impl<'a> PartialEq for dyn IServiceProvider + 'a {
36 fn eq(&self, other: &Self) -> bool {
37 std::ptr::addr_eq(self, other)
38 }
39}
40
41/// Boxes a service handle for [`IServiceProvider::get_service`] if it is the
42/// requested one. Meant for implementations:
43///
44/// ```ignore
45/// fn get_service(&self, service_type: TypeId) -> Option<Rc<dyn Any>> {
46/// service(service_type, || self.name_scope.clone())
47/// .or_else(|| service(service_type, || self.root_object_provider()))
48/// }
49/// ```
50pub fn service<T: 'static>(service_type: TypeId, handle: impl FnOnce() -> T) -> Option<Rc<dyn Any>> {
51 if service_type == TypeId::of::<T>() {
52 Some(Rc::new(handle()))
53 } else {
54 None
55 }
56}
57
58/// A service provider without services.
59pub struct EmptyServiceProvider;
60
61impl IServiceProvider for EmptyServiceProvider {
62 fn get_service(&self, _service_type: TypeId) -> Option<Rc<dyn Any>> {
63 None
64 }
65}