From 469a7ac0afc4d63768ab21fb692f0f4008bfe084 Mon Sep 17 00:00:00 2001 From: niedzwiedzw Date: Tue, 11 Jul 2023 21:41:52 +0200 Subject: [PATCH] better diagnostics for use_shared_state --- Cargo.toml | 3 +- packages/hooks/Cargo.toml | 1 + packages/hooks/src/use_shared_state.rs | 78 ++++++++++++++++++++++++-- 3 files changed, 75 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d69a74828..4f6561e4d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ futures-util = { version = "0.3", default-features = false } rustc-hash = "1.1.0" wasm-bindgen = "0.2.87" html_parser = "0.7.0" +thiserror = "1.0.30" # This is a "virtual package" # It is not meant to be published, but is used so "cargo run --example XYZ" works properly @@ -112,9 +113,9 @@ rand = { version = "0.8.4", features = ["small_rng"] } tokio = { version = "1.16.1", features = ["full"] } reqwest = { version = "0.11.9", features = ["json"] } fern = { version = "0.6.0", features = ["colored"] } -thiserror = "1.0.30" env_logger = "0.10.0" simple_logger = "4.0.0" +thiserror = { workspace = true } [profile.release] opt-level = 3 diff --git a/packages/hooks/Cargo.toml b/packages/hooks/Cargo.toml index 6a3205c68..198cdb74e 100644 --- a/packages/hooks/Cargo.toml +++ b/packages/hooks/Cargo.toml @@ -14,6 +14,7 @@ keywords = ["dom", "ui", "gui", "react"] dioxus-core = { workspace = true } futures-channel = { workspace = true } log = { workspace = true } +thiserror = { workspace = true } [dev-dependencies] diff --git a/packages/hooks/src/use_shared_state.rs b/packages/hooks/src/use_shared_state.rs index 3a3a4d639..c994e4817 100644 --- a/packages/hooks/src/use_shared_state.rs +++ b/packages/hooks/src/use_shared_state.rs @@ -121,30 +121,96 @@ pub struct UseSharedState { pub(crate) inner: Rc>>, } +#[derive(thiserror::Error, Debug)] +pub enum UseSharedStateError { + #[error("{type_name} is already borrowed, so it cannot be borrowed mutably.")] + AlreadyBorrowed { + source: core::cell::BorrowMutError, + type_name: &'static str, + }, + #[error("{type_name} is already borrowed mutably, so it cannot be borrowed anymore.")] + AlreadyBorrowedMutably { + source: core::cell::BorrowError, + type_name: &'static str, + }, +} + +pub type UseSharedStateResult = Result; + impl UseSharedState { /// Notify all consumers of the state that it has changed. (This is called automatically when you call "write") pub fn notify_consumers(&self) { self.inner.borrow_mut().notify_consumers(); } + /// Try reading the shared state + pub fn try_read(&self) -> UseSharedStateResult> { + self.inner + .try_borrow() + .map_err(|source| UseSharedStateError::AlreadyBorrowedMutably { + source, + type_name: std::any::type_name::(), + }) + .map(|value| Ref::map(value, |inner| &inner.value)) + } /// Read the shared value pub fn read(&self) -> Ref<'_, T> { - Ref::map(self.inner.borrow(), |inner| &inner.value) + match self.try_read() { + Ok(value) => value, + Err(message) => panic!( + "Reading the shared state failed: {}\n({:?})", + message, message + ), + } } + /// Try writing the shared state + pub fn try_write(&self) -> UseSharedStateResult> { + self.inner + .try_borrow_mut() + .map_err(|source| UseSharedStateError::AlreadyBorrowed { + source, + type_name: std::any::type_name::(), + }) + .map(|mut value| { + value.notify_consumers(); + RefMut::map(value, |inner| &mut inner.value) + }) + } /// Calling "write" will force the component to re-render /// /// // TODO: We prevent unncessary notifications only in the hook, but we should figure out some more global lock pub fn write(&self) -> RefMut<'_, T> { - let mut value = self.inner.borrow_mut(); - value.notify_consumers(); - RefMut::map(value, |inner| &mut inner.value) + match self.try_write() { + Ok(value) => value, + Err(message) => panic!( + "Writing to shared state failed: {}\n({:?})", + message, message + ), + } } - /// Allows the ability to write the value without forcing a re-render + /// Tries writing the value without forcing a re-render + pub fn try_write_silent(&self) -> UseSharedStateResult> { + self.inner + .try_borrow_mut() + .map_err(|source| UseSharedStateError::AlreadyBorrowed { + source, + type_name: std::any::type_name::(), + }) + .map(|value| RefMut::map(value, |inner| &mut inner.value)) + } + + /// Writes the value without forcing a re-render pub fn write_silent(&self) -> RefMut<'_, T> { - RefMut::map(self.inner.borrow_mut(), |inner| &mut inner.value) + match self.try_write_silent() { + Ok(value) => value, + Err(message) => panic!( + "Writing to shared state silently failed: {}\n({:?})", + message, message + ), + } } }