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