Skip to main content

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}