Skip to content

Commit

Permalink
refactor(core): return boolean on Manager::manage (#3682)
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasfernog committed Mar 13, 2022
1 parent c81534e commit 263b45e
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 34 deletions.
5 changes: 5 additions & 0 deletions .changes/state-manage-return-val.md
@@ -0,0 +1,5 @@
---
"tauri": patch
---

**Breaking change:** The `Manager::manage` function now returns a bool indicating whether the type is already managed or not.
22 changes: 13 additions & 9 deletions core/tauri/src/app.rs
Expand Up @@ -782,10 +782,10 @@ impl<R: Runtime> Builder<R> {
/// This method can be called any number of times as long as each call
/// refers to a different `T`.
///
/// Managed state can be retrieved by any request handler via the
/// [`State`](crate::State) request guard. In particular, if a value of type `T`
/// Managed state can be retrieved by any command handler via the
/// [`State`](crate::State) guard. In particular, if a value of type `T`
/// is managed by Tauri, adding `State<T>` to the list of arguments in a
/// request handler instructs Tauri to retrieve the managed value.
/// command handler instructs Tauri to retrieve the managed value.
///
/// # Panics
///
Expand All @@ -799,25 +799,29 @@ impl<R: Runtime> Builder<R> {
/// use std::{collections::HashMap, sync::Mutex};
/// use tauri::State;
/// // here we use Mutex to achieve interior mutability
/// struct Storage(Mutex<HashMap<u64, String>>);
/// struct Storage {
/// store: Mutex<HashMap<u64, String>>,
/// }
/// struct Connection;
/// struct DbConnection(Mutex<Option<Connection>>);
/// struct DbConnection {
/// db: Mutex<Option<Connection>>,
/// }
///
/// #[tauri::command]
/// fn connect(connection: State<DbConnection>) {
/// // initialize the connection, mutating the state with interior mutability
/// *connection.0.lock().unwrap() = Some(Connection {});
/// *connection.db.lock().unwrap() = Some(Connection {});
/// }
///
/// #[tauri::command]
/// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
/// // mutate the storage behind the Mutex
/// storage.0.lock().unwrap().insert(key, value);
/// storage.store.lock().unwrap().insert(key, value);
/// }
///
/// tauri::Builder::default()
/// .manage(Storage(Default::default()))
/// .manage(DbConnection(Default::default()))
/// .manage(Storage { store: Default::default() })
/// .manage(DbConnection { db: Default::default() })
/// .invoke_handler(tauri::generate_handler![connect, storage_insert])
/// // on an actual app, remove the string argument
/// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
Expand Down
104 changes: 99 additions & 5 deletions core/tauri/src/lib.rs
Expand Up @@ -544,15 +544,107 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
}

/// Add `state` to the state managed by the application.
/// See [`crate::Builder#manage`] for instructions.
fn manage<T>(&self, state: T)
///
/// This method can be called any number of times as long as each call
/// refers to a different `T`.
/// If a state for `T` is already managed, the function returns false and the value is ignored.
///
/// Managed state can be retrieved by any command handler via the
/// [`State`](crate::State) guard. In particular, if a value of type `T`
/// is managed by Tauri, adding `State<T>` to the list of arguments in a
/// command handler instructs Tauri to retrieve the managed value.
///
/// # Panics
///
/// Panics if state of type `T` is already being managed.
///
/// # Mutability
///
/// Since the managed state is global and must be [`Send`] + [`Sync`], mutations can only happen through interior mutability:
///
/// ```rust,no_run
/// use std::{collections::HashMap, sync::Mutex};
/// use tauri::State;
/// // here we use Mutex to achieve interior mutability
/// struct Storage {
/// store: Mutex<HashMap<u64, String>>,
/// }
/// struct Connection;
/// struct DbConnection {
/// db: Mutex<Option<Connection>>,
/// }
///
/// #[tauri::command]
/// fn connect(connection: State<DbConnection>) {
/// // initialize the connection, mutating the state with interior mutability
/// *connection.db.lock().unwrap() = Some(Connection {});
/// }
///
/// #[tauri::command]
/// fn storage_insert(key: u64, value: String, storage: State<Storage>) {
/// // mutate the storage behind the Mutex
/// storage.store.lock().unwrap().insert(key, value);
/// }
///
/// tauri::Builder::default()
/// .manage(Storage { store: Default::default() })
/// .manage(DbConnection { db: Default::default() })
/// .invoke_handler(tauri::generate_handler![connect, storage_insert])
/// // on an actual app, remove the string argument
/// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .expect("error while running tauri application");
/// ```
///
/// # Examples
///
/// ```rust,no_run
/// use tauri::{Manager, State};
///
/// struct MyInt(isize);
/// struct MyString(String);
///
/// #[tauri::command]
/// fn int_command(state: State<MyInt>) -> String {
/// format!("The stateful int is: {}", state.0)
/// }
///
/// #[tauri::command]
/// fn string_command<'r>(state: State<'r, MyString>) {
/// println!("state: {}", state.inner().0);
/// }
///
/// tauri::Builder::default()
/// .setup(|app| {
/// app.manage(MyInt(0));
/// app.manage(MyString("tauri".into()));
/// // `MyInt` is already managed, so `manage()` returns false
/// assert!(!app.manage(MyInt(1)));
/// // read the `MyInt` managed state with the turbofish syntax
/// let int = app.state::<MyInt>();
/// assert_eq!(int.0, 0);
/// // read the `MyString` managed state with the `State` guard
/// let val: State<MyString> = app.state();
/// assert_eq!(val.0, "tauri");
/// Ok(())
/// })
/// .invoke_handler(tauri::generate_handler![int_command, string_command])
/// // on an actual app, remove the string argument
/// .run(tauri::generate_context!("test/fixture/src-tauri/tauri.conf.json"))
/// .expect("error while running tauri application");
/// ```
fn manage<T>(&self, state: T) -> bool
where
T: Send + Sync + 'static,
{
self.manager().state().set(state);
self.manager().state().set(state)
}

/// Gets the managed state for the type `T`. Panics if the type is not managed.
/// Retrieves the managed state for the type `T`.
///
/// # Panics
///
/// Panics if the state for the type `T` has not been previously [managed](Self::manage).
/// Use [try_state](Self::try_state) for a non-panicking version.
fn state<T>(&self) -> State<'_, T>
where
T: Send + Sync + 'static,
Expand All @@ -565,7 +657,9 @@ pub trait Manager<R: Runtime>: sealed::ManagerBase<R> {
.expect("state() called before manage() for given type")
}

/// Tries to get the managed state for the type `T`. Returns `None` if the type is not managed.
/// Attempts to retrieve the managed state for the type `T`.
///
/// Returns `Some` if the state has previously been [managed](Self::manage). Otherwise returns `None`.
fn try_state<T>(&self) -> Option<State<'_, T>>
where
T: Send + Sync + 'static,
Expand Down
1 change: 1 addition & 0 deletions core/tauri/src/state.rs
Expand Up @@ -70,6 +70,7 @@ impl StateManager {

/// Gets the state associated with the specified type.
pub fn get<T: Send + Sync + 'static>(&self) -> State<'_, T> {
self.0.get::<T>();
State(
self
.0
Expand Down
39 changes: 19 additions & 20 deletions core/tauri/tests/restart/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 263b45e

Please sign in to comment.