tauri/resources/mod.rs
1// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
2// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
3// SPDX-License-Identifier: Apache-2.0
4// SPDX-License-Identifier: MIT
5
6// a modified version of https://github.com/denoland/deno/blob/0ae83847f498a2886ae32172e50fd5bdbab2f524/core/resources.rs#L220
7
8pub(crate) mod plugin;
9
10use std::{
11  any::{type_name, Any, TypeId},
12  borrow::Cow,
13  collections::BTreeMap,
14  sync::Arc,
15};
16
17/// Resources are Rust objects that are stored in [ResourceTable] and managed by tauri.
18///
19/// They are identified in JS by a numeric ID (the resource ID, or rid).
20/// Resources can be created in commands. Resources can also be retrieved in commands by
21/// their rid. Resources are thread-safe.
22///
23/// Resources are reference counted in Rust. This means that they can be
24/// cloned and passed around. When the last reference is dropped, the resource
25/// is automatically closed. As long as the resource exists in the resource
26/// table, the reference count is at least 1.
27pub trait Resource: Any + 'static + Send + Sync {
28  /// Returns a string representation of the resource. The default implementation
29  /// returns the Rust type name, but specific resource types may override this
30  /// trait method.
31  fn name(&self) -> Cow<'_, str> {
32    type_name::<Self>().into()
33  }
34
35  /// Resources may implement the `close()` trait method if they need to do
36  /// resource specific clean-ups, such as cancelling pending futures, after a
37  /// resource has been removed from the resource table.
38  fn close(self: Arc<Self>) {}
39}
40
41impl dyn Resource {
42  #[inline(always)]
43  fn is<T: Resource>(&self) -> bool {
44    self.type_id() == TypeId::of::<T>()
45  }
46
47  #[inline(always)]
48  pub(crate) fn downcast_arc<'a, T: Resource>(self: &'a Arc<Self>) -> Option<&'a Arc<T>> {
49    if self.is::<T>() {
50      // A resource is stored as `Arc<T>` in a BTreeMap
51      // and is safe to cast to `Arc<T>` because of the runtime
52      // check done in `self.is::<T>()`
53      let ptr = self as *const Arc<_> as *const Arc<T>;
54      Some(unsafe { &*ptr })
55    } else {
56      None
57    }
58  }
59}
60
61/// A `ResourceId` is an integer value referencing a resource. It could be
62/// considered to be the tauri equivalent of a `file descriptor` in POSIX like
63/// operating systems.
64pub type ResourceId = u32;
65
66/// Map-like data structure storing Tauri's resources (equivalent to file
67/// descriptors).
68///
69/// Provides basic methods for element access. A resource can be of any type.
70/// Different types of resources can be stored in the same map, and provided
71/// with a name for description.
72///
73/// Each resource is identified through a _resource ID (rid)_, which acts as
74/// the key in the map.
75#[derive(Default)]
76pub struct ResourceTable {
77  index: BTreeMap<ResourceId, Arc<dyn Resource>>,
78}
79
80impl ResourceTable {
81  fn new_random_rid() -> u32 {
82    let mut bytes = [0_u8; 4];
83    getrandom::fill(&mut bytes).expect("failed to get random bytes");
84    u32::from_ne_bytes(bytes)
85  }
86
87  /// Inserts resource into the resource table, which takes ownership of it.
88  ///
89  /// The resource type is erased at runtime and must be statically known
90  /// when retrieving it through `get()`.
91  ///
92  /// Returns a unique resource ID, which acts as a key for this resource.
93  pub fn add<T: Resource>(&mut self, resource: T) -> ResourceId {
94    self.add_arc(Arc::new(resource))
95  }
96
97  /// Inserts a `Arc`-wrapped resource into the resource table.
98  ///
99  /// The resource type is erased at runtime and must be statically known
100  /// when retrieving it through `get()`.
101  ///
102  /// Returns a unique resource ID, which acts as a key for this resource.
103  pub fn add_arc<T: Resource>(&mut self, resource: Arc<T>) -> ResourceId {
104    let resource = resource as Arc<dyn Resource>;
105    self.add_arc_dyn(resource)
106  }
107
108  /// Inserts a `Arc`-wrapped resource into the resource table.
109  ///
110  /// The resource type is erased at runtime and must be statically known
111  /// when retrieving it through `get()`.
112  ///
113  /// Returns a unique resource ID, which acts as a key for this resource.
114  pub fn add_arc_dyn(&mut self, resource: Arc<dyn Resource>) -> ResourceId {
115    let mut rid = Self::new_random_rid();
116    while self.index.contains_key(&rid) {
117      rid = Self::new_random_rid();
118    }
119
120    let removed_resource = self.index.insert(rid, resource);
121    assert!(removed_resource.is_none());
122    rid
123  }
124
125  /// Returns true if any resource with the given `rid` exists.
126  pub fn has(&self, rid: ResourceId) -> bool {
127    self.index.contains_key(&rid)
128  }
129
130  /// Returns a reference counted pointer to the resource of type `T` with the
131  /// given `rid`. If `rid` is not present or has a type different than `T`,
132  /// this function returns [`Error::BadResourceId`](crate::Error::BadResourceId).
133  pub fn get<T: Resource>(&self, rid: ResourceId) -> crate::Result<Arc<T>> {
134    self
135      .index
136      .get(&rid)
137      .and_then(|rc| rc.downcast_arc::<T>())
138      .cloned()
139      .ok_or_else(|| crate::Error::BadResourceId(rid))
140  }
141
142  /// Returns a reference counted pointer to the resource of the given `rid`.
143  /// If `rid` is not present, this function returns [`Error::BadResourceId`].
144  pub fn get_any(&self, rid: ResourceId) -> crate::Result<Arc<dyn Resource>> {
145    self
146      .index
147      .get(&rid)
148      .ok_or_else(|| crate::Error::BadResourceId(rid))
149      .cloned()
150  }
151
152  /// Replaces a resource with a new resource.
153  ///
154  /// Panics if the resource does not exist.
155  pub fn replace<T: Resource>(&mut self, rid: ResourceId, resource: T) {
156    let result = self
157      .index
158      .insert(rid, Arc::new(resource) as Arc<dyn Resource>);
159    assert!(result.is_some());
160  }
161
162  /// Removes a resource of type `T` from the resource table and returns it.
163  /// If a resource with the given `rid` exists but its type does not match `T`,
164  /// it is not removed from the resource table. Note that the resource's
165  /// `close()` method is *not* called.
166  ///
167  /// Also note that there might be a case where
168  /// the returned `Arc<T>` is referenced by other variables. That is, we cannot
169  /// assume that `Arc::strong_count(&returned_arc)` is always equal to 1 on success.
170  /// In particular, be really careful when you want to extract the inner value of
171  /// type `T` from `Arc<T>`.
172  pub fn take<T: Resource>(&mut self, rid: ResourceId) -> crate::Result<Arc<T>> {
173    let resource = self.get::<T>(rid)?;
174    self.index.remove(&rid);
175    Ok(resource)
176  }
177
178  /// Removes a resource from the resource table and returns it. Note that the
179  /// resource's `close()` method is *not* called.
180  ///
181  /// Also note that there might be a
182  /// case where the returned `Arc<T>` is referenced by other variables. That is,
183  /// we cannot assume that `Arc::strong_count(&returned_arc)` is always equal to 1
184  /// on success. In particular, be really careful when you want to extract the
185  /// inner value of type `T` from `Arc<T>`.
186  pub fn take_any(&mut self, rid: ResourceId) -> crate::Result<Arc<dyn Resource>> {
187    self
188      .index
189      .remove(&rid)
190      .ok_or_else(|| crate::Error::BadResourceId(rid))
191  }
192
193  /// Returns an iterator that yields a `(id, name)` pair for every resource
194  /// that's currently in the resource table. This can be used for debugging
195  /// purposes. Note that the order in
196  /// which items appear is not specified.
197  pub fn names(&self) -> impl Iterator<Item = (ResourceId, Cow<'_, str>)> {
198    self
199      .index
200      .iter()
201      .map(|(&id, resource)| (id, resource.name()))
202  }
203
204  /// Removes the resource with the given `rid` from the resource table. If the
205  /// only reference to this resource existed in the resource table, this will
206  /// cause the resource to be dropped. However, since resources are reference
207  /// counted, therefore pending ops are not automatically cancelled. A resource
208  /// may implement the `close()` method to perform clean-ups such as canceling
209  /// ops.
210  pub fn close(&mut self, rid: ResourceId) -> crate::Result<()> {
211    self
212      .index
213      .remove(&rid)
214      .ok_or_else(|| crate::Error::BadResourceId(rid))
215      .map(|resource| resource.close())
216  }
217
218  /// Removes and frees all resources stored. Note that the
219  /// resource's `close()` method is *not* called.
220  pub(crate) fn clear(&mut self) {
221    self.index.clear()
222  }
223}