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}