Skip to main content

wire_framework/resource/
mod.rs

1use std::{any::TypeId, fmt};
2
3pub use self::{resource_id::ResourceId, unique::Unique};
4
5mod resource_id;
6mod unique;
7
8/// A trait for anything that can be stored (and retrieved) as a resource.
9///
10/// Typically, the type that implements this trait also should implement `Clone`
11/// since the same resource may be requested by several tasks and thus it would be an additional
12/// bound on most methods that work with [`Resource`].
13///
14/// # Example
15///
16/// ```
17/// # use wire_framework::resource::Resource;
18/// # use std::sync::Arc;
19///
20/// /// An abstract interface you want to share.
21/// /// Normally you want the interface to be thread-safe.
22/// trait MyInterface: 'static + Send + Sync {
23///     fn do_something(&self);
24/// }
25///
26/// /// Resource wrapper.
27/// #[derive(Clone)]
28/// struct MyResource(Arc<dyn MyInterface>);
29///
30/// impl Resource for MyResource {
31///     fn name() -> String {
32///         // It is a helpful practice to follow a structured naming pattern for resource names.
33///         // For example, you can use a certain prefix for all resources related to a some component, e.g. `api`.
34///         "common/my_resource".to_string()
35///     }
36/// }
37/// ```
38pub trait Resource: 'static + Send + Sync + std::any::Any {
39    /// Invoked after the wiring phase of the service is done.
40    /// Can be used to perform additional resource preparation, knowing that the resource
41    /// is guaranteed to be requested by all the tasks that need it.
42    fn on_resource_wired(&mut self) {}
43
44    /// Returns the name of the resource.
45    /// Used for logging purposes.
46    fn name() -> String;
47}
48
49/// Internal, object-safe version of [`Resource`].
50/// Used to store resources in the node without knowing their exact type.
51///
52/// This trait is implemented for any type that implements [`Resource`], so there is no need to
53/// implement it manually.
54pub(crate) trait StoredResource: 'static + std::any::Any + Send + Sync {
55    /// An object-safe version of [`Resource::name`].
56    fn stored_resource_id(&self) -> ResourceId;
57
58    /// An object-safe version of [`Resource::on_resource_wired`].
59    fn stored_resource_wired(&mut self);
60}
61
62impl fmt::Debug for dyn StoredResource {
63    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64        f.debug_struct("Resource")
65            .field("resource_id", &self.stored_resource_id())
66            .finish()
67    }
68}
69
70impl<T: Resource> StoredResource for T {
71    fn stored_resource_id(&self) -> ResourceId {
72        ResourceId::of::<T>()
73    }
74
75    fn stored_resource_wired(&mut self) {
76        Resource::on_resource_wired(self);
77    }
78}
79
80impl dyn StoredResource {
81    /// Reimplementation of `Any::downcast_ref`.
82    /// Returns `Some` if the type is correct, and `None` otherwise.
83    // Note: This method is required as we cannot store objects as, for example, `dyn StoredResource + Any`,
84    // so we don't have access to `Any::downcast_ref` within the node.
85    pub(crate) fn downcast_ref<T: Resource>(&self) -> Option<&T> {
86        if self.type_id() == TypeId::of::<T>() {
87            // SAFETY: We just checked that the type is correct.
88            unsafe { Some(&*(self as *const dyn StoredResource as *const T)) }
89        } else {
90            None
91        }
92    }
93}