From bf095df55aa27fb22c9240ddf8d673cfe0a4a2db Mon Sep 17 00:00:00 2001 From: Amr Bashir Date: Tue, 21 Nov 2023 12:57:35 +0200 Subject: [PATCH] feat: expose `Manager::resources_table` (#8276) Co-authored-by: Lucas Fernandes Nogueira --- .changes/tauri-resources-table.md | 5 ++ core/tauri-macros/src/lib.rs | 4 +- core/tauri/src/app.rs | 2 +- core/tauri/src/lib.rs | 7 ++ core/tauri/src/menu/plugin.rs | 44 ++++++------- core/tauri/src/resources/mod.rs | 100 +++++++++++++++++++++++++---- core/tauri/src/resources/plugin.rs | 4 +- core/tauri/src/state.rs | 1 - core/tauri/src/tray/plugin.rs | 20 +++--- examples/api/src/App.svelte | 17 +++-- 10 files changed, 146 insertions(+), 58 deletions(-) create mode 100644 .changes/tauri-resources-table.md diff --git a/.changes/tauri-resources-table.md b/.changes/tauri-resources-table.md new file mode 100644 index 000000000..2f98fc883 --- /dev/null +++ b/.changes/tauri-resources-table.md @@ -0,0 +1,5 @@ +--- +'tauri': 'patch:feat' +--- + +Exposed `Manager::resources_table` to access the resources table used by tauri, which could be used by plugins or app authors to store their resources and retrieve it later using an id. diff --git a/core/tauri-macros/src/lib.rs b/core/tauri-macros/src/lib.rs index 104dd31a6..990dc6aff 100644 --- a/core/tauri-macros/src/lib.rs +++ b/core/tauri-macros/src/lib.rs @@ -114,14 +114,14 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre /// ```ignore /// let rid = 23; /// let kind = ItemKind::Check; -/// let resources_table = app.manager.resources_table(); +/// let resources_table = app.resources_table(); /// do_menu_item!(|i| i.set_text(text)) /// ``` /// which will expand into: /// ```ignore /// let rid = 23; /// let kind = ItemKind::Check; -/// let resources_table = app.manager.resources_table(); +/// let resources_table = app.resources_table(); /// match kind { /// ItemKind::Submenu => { /// let i = resources_table.get::>(rid)?; diff --git a/core/tauri/src/app.rs b/core/tauri/src/app.rs index 364b9a41b..658b99c01 100644 --- a/core/tauri/src/app.rs +++ b/core/tauri/src/app.rs @@ -773,7 +773,7 @@ macro_rules! shared_app_impl { pub fn cleanup_before_exit(&self) { #[cfg(all(desktop, feature = "tray-icon"))] self.manager.tray.icons.lock().unwrap().clear(); - self.manager.resources_table().clear(); + self.resources_table().clear(); } } }; diff --git a/core/tauri/src/lib.rs b/core/tauri/src/lib.rs index 7acc2595b..36eee36a3 100644 --- a/core/tauri/src/lib.rs +++ b/core/tauri/src/lib.rs @@ -65,6 +65,7 @@ pub use cocoa; #[doc(hidden)] pub use embed_plist; pub use error::{Error, Result}; +pub use resources::{Resource, ResourceId, ResourceTable}; #[cfg(target_os = "ios")] #[doc(hidden)] pub use swift_rs; @@ -165,6 +166,7 @@ use serde::{Deserialize, Serialize}; use std::{ collections::HashMap, fmt::{self, Debug}, + sync::MutexGuard, }; #[cfg(feature = "wry")] @@ -809,6 +811,11 @@ pub trait Manager: sealed::ManagerBase { self.manager().state.try_get() } + /// Get a reference to the resources table. + fn resources_table(&self) -> MutexGuard<'_, ResourceTable> { + self.manager().resources_table() + } + /// Gets the managed [`Env`]. fn env(&self) -> Env { self.state::().inner().clone() diff --git a/core/tauri/src/menu/plugin.rs b/core/tauri/src/menu/plugin.rs index c659bf349..ad10c4d9e 100644 --- a/core/tauri/src/menu/plugin.rs +++ b/core/tauri/src/menu/plugin.rs @@ -334,7 +334,7 @@ fn new( handler: Channel, ) -> crate::Result<(ResourceId, MenuId)> { let options = options.unwrap_or_default(); - let mut resources_table = app.manager.resources_table(); + let mut resources_table = app.resources_table(); let (rid, id) = match kind { ItemKind::Menu => { @@ -439,7 +439,7 @@ fn append( kind: ItemKind, items: Vec, ) -> crate::Result<()> { - let resources_table = window.manager.resources_table(); + let resources_table = window.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -466,7 +466,7 @@ fn prepend( kind: ItemKind, items: Vec, ) -> crate::Result<()> { - let resources_table = window.manager.resources_table(); + let resources_table = window.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -494,7 +494,7 @@ fn insert( items: Vec, mut position: usize, ) -> crate::Result<()> { - let resources_table = window.manager.resources_table(); + let resources_table = window.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -523,7 +523,7 @@ fn remove( menu_kind: ItemKind, item: (ResourceId, ItemKind), ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let (rid, kind) = item; match menu_kind { ItemKind::Menu => { @@ -561,7 +561,7 @@ fn remove_at( kind: ItemKind, position: usize, ) -> crate::Result> { - let mut resources_table = app.manager.resources_table(); + let mut resources_table = app.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -587,7 +587,7 @@ fn items( rid: ResourceId, kind: ItemKind, ) -> crate::Result> { - let mut resources_table = app.manager.resources_table(); + let mut resources_table = app.resources_table(); let items = match kind { ItemKind::Menu => resources_table.get::>(rid)?.items()?, ItemKind::Submenu => resources_table.get::>(rid)?.items()?, @@ -609,7 +609,7 @@ fn get( kind: ItemKind, id: MenuId, ) -> crate::Result> { - let mut resources_table = app.manager.resources_table(); + let mut resources_table = app.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -643,7 +643,7 @@ async fn popup( .unwrap_or(Some(current_window)); if let Some(window) = window { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); match kind { ItemKind::Menu => { let menu = resources_table.get::>(rid)?; @@ -662,7 +662,7 @@ async fn popup( #[command(root = "crate")] fn default(app: AppHandle) -> crate::Result<(ResourceId, MenuId)> { - let mut resources_table = app.manager.resources_table(); + let mut resources_table = app.resources_table(); let menu = Menu::default(&app)?; let id = menu.id().clone(); let rid = resources_table.add(menu); @@ -674,7 +674,7 @@ async fn set_as_app_menu( app: AppHandle, rid: ResourceId, ) -> crate::Result> { - let mut resources_table = app.manager.resources_table(); + let mut resources_table = app.resources_table(); let menu = resources_table.get::>(rid)?; if let Some(menu) = menu.set_as_app_menu()? { let id = menu.id().clone(); @@ -696,7 +696,7 @@ async fn set_as_window_menu( .unwrap_or(Some(current_window)); if let Some(window) = window { - let mut resources_table = app.manager.resources_table(); + let mut resources_table = app.resources_table(); let menu = resources_table.get::>(rid)?; if let Some(menu) = menu.set_as_window_menu(&window)? { let id = menu.id().clone(); @@ -709,7 +709,7 @@ async fn set_as_window_menu( #[command(root = "crate")] fn text(app: AppHandle, rid: ResourceId, kind: ItemKind) -> crate::Result { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); do_menu_item!(resources_table, rid, kind, |i| i.text()) } @@ -720,7 +720,7 @@ fn set_text( kind: ItemKind, text: String, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); do_menu_item!(resources_table, rid, kind, |i| i.set_text(text)) } @@ -730,7 +730,7 @@ fn is_enabled( rid: ResourceId, kind: ItemKind, ) -> crate::Result { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); do_menu_item!(resources_table, rid, kind, |i| i.is_enabled(), !Predefined) } @@ -741,7 +741,7 @@ fn set_enabled( kind: ItemKind, enabled: bool, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); do_menu_item!( resources_table, rid, @@ -758,7 +758,7 @@ fn set_accelerator( kind: ItemKind, accelerator: Option, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); do_menu_item!( resources_table, rid, @@ -775,7 +775,7 @@ fn set_as_windows_menu_for_nsapp( ) -> crate::Result<()> { #[cfg(target_os = "macos")] { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let submenu = resources_table.get::>(rid)?; submenu.set_as_help_menu_for_nsapp()?; } @@ -789,7 +789,7 @@ fn set_as_windows_menu_for_nsapp( fn set_as_help_menu_for_nsapp(app: AppHandle, rid: ResourceId) -> crate::Result<()> { #[cfg(target_os = "macos")] { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let submenu = resources_table.get::>(rid)?; submenu.set_as_help_menu_for_nsapp()?; } @@ -802,14 +802,14 @@ fn set_as_help_menu_for_nsapp(app: AppHandle, rid: ResourceId) -> #[command(root = "crate")] fn is_checked(app: AppHandle, rid: ResourceId) -> crate::Result { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let check_item = resources_table.get::>(rid)?; check_item.is_checked() } #[command(root = "crate")] fn set_checked(app: AppHandle, rid: ResourceId, checked: bool) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let check_item = resources_table.get::>(rid)?; check_item.set_checked(checked) } @@ -820,7 +820,7 @@ fn set_icon( rid: ResourceId, icon: Option, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let icon_item = resources_table.get::>(rid)?; match icon { Some(Icon::Native(icon)) => icon_item.set_native_icon(Some(icon)), diff --git a/core/tauri/src/resources/mod.rs b/core/tauri/src/resources/mod.rs index e178fed79..0829374f5 100644 --- a/core/tauri/src/resources/mod.rs +++ b/core/tauri/src/resources/mod.rs @@ -24,7 +24,7 @@ use std::{ /// cloned and passed around. When the last reference is dropped, the resource /// is automatically closed. As long as the resource exists in the resource /// table, the reference count is at least 1. -pub(crate) trait Resource: Any + 'static + Send + Sync { +pub trait Resource: Any + 'static + Send + Sync { /// Returns a string representation of the resource. The default implementation /// returns the Rust type name, but specific resource types may override this /// trait method. @@ -60,9 +60,8 @@ impl dyn Resource { /// A `ResourceId` is an integer value referencing a resource. It could be /// considered to be the tauri equivalent of a `file descriptor` in POSIX like -/// operating systems. Elsewhere in the code base it is commonly abbreviated -/// to `rid`. -pub(crate) type ResourceId = u32; +/// operating systems. +pub type ResourceId = u32; /// Map-like data structure storing Tauri's resources (equivalent to file /// descriptors). @@ -74,9 +73,9 @@ pub(crate) type ResourceId = u32; /// Each resource is identified through a _resource ID (rid)_, which acts as /// the key in the map. #[derive(Default)] -pub(crate) struct ResourceTable { - pub(crate) index: BTreeMap>, - pub(crate) next_rid: ResourceId, +pub struct ResourceTable { + index: BTreeMap>, + next_rid: ResourceId, } impl ResourceTable { @@ -86,7 +85,7 @@ impl ResourceTable { /// when retrieving it through `get()`. /// /// Returns a unique resource ID, which acts as a key for this resource. - pub(crate) fn add(&mut self, resource: T) -> ResourceId { + pub fn add(&mut self, resource: T) -> ResourceId { self.add_arc(Arc::new(resource)) } @@ -96,24 +95,34 @@ impl ResourceTable { /// when retrieving it through `get()`. /// /// Returns a unique resource ID, which acts as a key for this resource. - pub(crate) fn add_arc(&mut self, resource: Arc) -> ResourceId { + pub fn add_arc(&mut self, resource: Arc) -> ResourceId { let resource = resource as Arc; self.add_arc_dyn(resource) } - pub(crate) fn add_arc_dyn(&mut self, resource: Arc) -> ResourceId { + /// Inserts a `Arc`-wrapped resource into the resource table. + /// + /// The resource type is erased at runtime and must be statically known + /// when retrieving it through `get()`. + /// + /// Returns a unique resource ID, which acts as a key for this resource. + pub fn add_arc_dyn(&mut self, resource: Arc) -> ResourceId { let rid = self.next_rid; let removed_resource = self.index.insert(rid, resource); - // TODO: add replace method if needed assert!(removed_resource.is_none()); self.next_rid += 1; rid } + /// Returns true if any resource with the given `rid` exists. + pub fn has(&self, rid: ResourceId) -> bool { + self.index.contains_key(&rid) + } + /// Returns a reference counted pointer to the resource of type `T` with the /// given `rid`. If `rid` is not present or has a type different than `T`, /// this function returns [`Error::BadResourceId`]. - pub(crate) fn get(&self, rid: ResourceId) -> Result, Error> { + pub fn get(&self, rid: ResourceId) -> Result, Error> { self .index .get(&rid) @@ -122,13 +131,75 @@ impl ResourceTable { .ok_or_else(|| Error::BadResourceId(rid)) } + /// Returns a reference counted pointer to the resource of the given `rid`. + /// If `rid` is not present, this function returns [`Error::BadResourceId`]. + pub fn get_any(&self, rid: ResourceId) -> Result, Error> { + self + .index + .get(&rid) + .map(Clone::clone) + .ok_or_else(|| Error::BadResourceId(rid)) + } + + /// Replaces a resource with a new resource. + /// + /// Panics if the resource does not exist. + pub fn replace(&mut self, rid: ResourceId, resource: T) { + let result = self + .index + .insert(rid, Arc::new(resource) as Arc); + assert!(result.is_some()); + } + + /// Removes a resource of type `T` from the resource table and returns it. + /// If a resource with the given `rid` exists but its type does not match `T`, + /// it is not removed from the resource table. Note that the resource's + /// `close()` method is *not* called. + /// + /// Also note that there might be a case where + /// the returned `Arc` is referenced by other variables. That is, we cannot + /// assume that `Arc::strong_count(&returned_arc)` is always equal to 1 on success. + /// In particular, be really careful when you want to extract the inner value of + /// type `T` from `Arc`. + pub fn take(&mut self, rid: ResourceId) -> Result, Error> { + let resource = self.get::(rid)?; + self.index.remove(&rid); + Ok(resource) + } + + /// Removes a resource from the resource table and returns it. Note that the + /// resource's `close()` method is *not* called. + /// + /// Also note that there might be a + /// case where the returned `Arc` is referenced by other variables. That is, + /// we cannot assume that `Arc::strong_count(&returned_arc)` is always equal to 1 + /// on success. In particular, be really careful when you want to extract the + /// inner value of type `T` from `Arc`. + pub fn take_any(&mut self, rid: ResourceId) -> Result, Error> { + self + .index + .remove(&rid) + .ok_or_else(|| Error::BadResourceId(rid)) + } + + /// Returns an iterator that yields a `(id, name)` pair for every resource + /// that's currently in the resource table. This can be used for debugging + /// purposes. Note that the order in + /// which items appear is not specified. + pub fn names(&self) -> impl Iterator)> { + self + .index + .iter() + .map(|(&id, resource)| (id, resource.name())) + } + /// Removes the resource with the given `rid` from the resource table. If the /// only reference to this resource existed in the resource table, this will /// cause the resource to be dropped. However, since resources are reference /// counted, therefore pending ops are not automatically cancelled. A resource /// may implement the `close()` method to perform clean-ups such as canceling /// ops. - pub(crate) fn close(&mut self, rid: ResourceId) -> Result<(), Error> { + pub fn close(&mut self, rid: ResourceId) -> Result<(), Error> { self .index .remove(&rid) @@ -136,7 +207,8 @@ impl ResourceTable { .map(|resource| resource.close()) } - /// Removes and frees all resources stored. + /// Removes and frees all resources stored. Note that the + /// resource's `close()` method is *not* called. pub(crate) fn clear(&mut self) { self.index.clear() } diff --git a/core/tauri/src/resources/plugin.rs b/core/tauri/src/resources/plugin.rs index 1b8932e21..863352fb6 100644 --- a/core/tauri/src/resources/plugin.rs +++ b/core/tauri/src/resources/plugin.rs @@ -5,14 +5,14 @@ use crate::{ command, plugin::{Builder, TauriPlugin}, - AppHandle, Runtime, + AppHandle, Manager, Runtime, }; use super::ResourceId; #[command(root = "crate")] fn close(app: AppHandle, rid: ResourceId) -> crate::Result<()> { - app.manager.resources_table().close(rid) + app.resources_table().close(rid) } pub(crate) fn init() -> TauriPlugin { diff --git a/core/tauri/src/state.rs b/core/tauri/src/state.rs index 098014234..c4bb85ade 100644 --- a/core/tauri/src/state.rs +++ b/core/tauri/src/state.rs @@ -72,7 +72,6 @@ impl StateManager { /// Gets the state associated with the specified type. pub fn get(&self) -> State<'_, T> { - self.0.get::(); State( self .0 diff --git a/core/tauri/src/tray/plugin.rs b/core/tauri/src/tray/plugin.rs index 03d4ebd55..5ca61a10c 100644 --- a/core/tauri/src/tray/plugin.rs +++ b/core/tauri/src/tray/plugin.rs @@ -13,7 +13,7 @@ use crate::{ plugin::{Builder, TauriPlugin}, resources::ResourceId, tray::TrayIconBuilder, - AppHandle, IconDto, Runtime, + AppHandle, IconDto, Manager, Runtime, }; use super::TrayIcon; @@ -47,7 +47,7 @@ fn new( let _ = handler.send(e); }); - let mut resources_table = app.manager.resources_table(); + let mut resources_table = app.resources_table(); if let Some((rid, kind)) = options.menu { match kind { @@ -94,7 +94,7 @@ fn set_icon( rid: ResourceId, icon: Option, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_icon(icon.map(Into::into)) } @@ -105,7 +105,7 @@ fn set_menu( rid: ResourceId, menu: Option<(ResourceId, ItemKind)>, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; if let Some((rid, kind)) = menu { match kind { @@ -131,7 +131,7 @@ fn set_tooltip( rid: ResourceId, tooltip: Option, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_tooltip(tooltip) } @@ -142,14 +142,14 @@ fn set_title( rid: ResourceId, title: Option, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_title(title) } #[command(root = "crate")] fn set_visible(app: AppHandle, rid: ResourceId, visible: bool) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_visible(visible) } @@ -160,7 +160,7 @@ fn set_temp_dir_path( rid: ResourceId, path: Option, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_temp_dir_path(path) } @@ -171,7 +171,7 @@ fn set_icon_as_template( rid: ResourceId, as_template: bool, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_icon_as_template(as_template) } @@ -182,7 +182,7 @@ fn set_show_menu_on_left_click( rid: ResourceId, on_left: bool, ) -> crate::Result<()> { - let resources_table = app.manager.resources_table(); + let resources_table = app.resources_table(); let tray = resources_table.get::>(rid)?; tray.set_show_menu_on_left_click(on_left) } diff --git a/examples/api/src/App.svelte b/examples/api/src/App.svelte index 5a3bb85b4..5f9f3ede7 100644 --- a/examples/api/src/App.svelte +++ b/examples/api/src/App.svelte @@ -1,5 +1,5 @@