Explorar el Código

better diagnostics for use_shared_state

niedzwiedzw hace 1 año
padre
commit
469a7ac0af
Se han modificado 3 ficheros con 75 adiciones y 7 borrados
  1. 2 1
      Cargo.toml
  2. 1 0
      packages/hooks/Cargo.toml
  3. 72 6
      packages/hooks/src/use_shared_state.rs

+ 2 - 1
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

+ 1 - 0
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]

+ 72 - 6
packages/hooks/src/use_shared_state.rs

@@ -121,30 +121,96 @@ pub struct UseSharedState<T> {
     pub(crate) inner: Rc<RefCell<ProvidedStateInner<T>>>,
 }
 
+#[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<T> = Result<T, UseSharedStateError>;
+
 impl<T> UseSharedState<T> {
     /// 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<Ref<'_, T>> {
+        self.inner
+            .try_borrow()
+            .map_err(|source| UseSharedStateError::AlreadyBorrowedMutably {
+                source,
+                type_name: std::any::type_name::<Self>(),
+            })
+            .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<RefMut<'_, T>> {
+        self.inner
+            .try_borrow_mut()
+            .map_err(|source| UseSharedStateError::AlreadyBorrowed {
+                source,
+                type_name: std::any::type_name::<Self>(),
+            })
+            .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<RefMut<'_, T>> {
+        self.inner
+            .try_borrow_mut()
+            .map_err(|source| UseSharedStateError::AlreadyBorrowed {
+                source,
+                type_name: std::any::type_name::<Self>(),
+            })
+            .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
+            ),
+        }
     }
 }