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}