Merge pull request #1187 from Niedzwiedzw/feature/use-shared-state-better-diagnostics
better diagnostics for use_shared_state
This commit is contained in:
11 files changed
+167
-19
No files matched your search
+2
-1
@@ -81,6 +81,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.40"
|
||||
|
||||
# This is a "virtual package"
|
||||
# It is not meant to be published, but is used so "cargo run --example XYZ" works properly
|
||||
@@ -117,6 +118,6 @@ 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 }
|
||||
@@ -10,7 +10,7 @@ license = "MIT OR Apache-2.0"
|
||||
[dependencies]
|
||||
# cli core
|
||||
clap = { version = "4.2", features = ["derive"] }
|
||||
thiserror = "1.0.30"
|
||||
thiserror = { workspace = true }
|
||||
wasm-bindgen-cli-support = "0.2"
|
||||
colored = "2.0.0"
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ dioxus-hot-reload = { workspace = true, optional = true }
|
||||
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
thiserror = "1.0.30"
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
wry = { version = "0.28.0" }
|
||||
futures-channel = { workspace = true }
|
||||
|
||||
@@ -33,7 +33,7 @@ futures-util = { workspace = true }
|
||||
log = { workspace = true }
|
||||
rand = { version = "0.8.4", features = ["small_rng"] }
|
||||
criterion = "0.3.5"
|
||||
thiserror = "1.0.30"
|
||||
thiserror = { workspace = true }
|
||||
env_logger = "0.10.0"
|
||||
tokio = { workspace = true, features = ["full"] }
|
||||
# dioxus-edit-stream = { workspace = true }
|
||||
|
||||
@@ -39,7 +39,7 @@ dioxus-router = { workspace = true, optional = true }
|
||||
|
||||
log = { workspace = true }
|
||||
once_cell = "1.17.1"
|
||||
thiserror = "1.0.40"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["full"], optional = true }
|
||||
object-pool = "0.5.4"
|
||||
anymap = "0.12.1"
|
||||
|
||||
@@ -9,12 +9,16 @@ repository = "https://github.com/DioxusLabs/dioxus/"
|
||||
homepage = "https://dioxuslabs.com"
|
||||
keywords = ["dom", "ui", "gui", "react"]
|
||||
|
||||
[features]
|
||||
default = []
|
||||
nightly-features = []
|
||||
|
||||
[dependencies]
|
||||
dioxus-core = { workspace = true }
|
||||
futures-channel = { workspace = true }
|
||||
log = { workspace = true }
|
||||
|
||||
thiserror = { workspace = true }
|
||||
debug-cell = { git = "https://github.com/Niedzwiedzw/debug-cell", rev = "3352a1f8aff19f56f5e3b2018200a3338fd43d2e" } # waiting for the merge / official DioxusLabs fork
|
||||
|
||||
[dev-dependencies]
|
||||
futures-util = { workspace = true, default-features = false }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#![cfg_attr(feature = "nightly-features", feature(debug_refcell))]
|
||||
|
||||
#[macro_export]
|
||||
/// A helper macro for using hooks and properties in async environements.
|
||||
///
|
||||
|
||||
@@ -1,11 +1,79 @@
|
||||
use self::error::{UseSharedStateError, UseSharedStateResult};
|
||||
use dioxus_core::{ScopeId, ScopeState};
|
||||
use std::{
|
||||
cell::{Ref, RefCell, RefMut},
|
||||
collections::HashSet,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
use std::{collections::HashSet, rc::Rc, sync::Arc};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub use debug_cell::{
|
||||
error::{BorrowError, BorrowMutError},
|
||||
Ref, RefCell, RefMut,
|
||||
};
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub use std::cell::{BorrowError, BorrowMutError, Ref, RefCell, RefMut};
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! debug_location {
|
||||
() => {{
|
||||
#[cfg(debug_assertions)]
|
||||
{
|
||||
std::panic::Location::caller()
|
||||
}
|
||||
#[cfg(not(debug_assertions))]
|
||||
{
|
||||
()
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
pub mod error {
|
||||
fn locations_display(locations: &[&'static std::panic::Location<'static>]) -> String {
|
||||
locations
|
||||
.iter()
|
||||
.map(|location| format!(" - {location}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
#[derive(thiserror::Error, Debug)]
|
||||
pub enum UseSharedStateError {
|
||||
#[cfg_attr(
|
||||
debug_assertions,
|
||||
error(
|
||||
"[{0}] {1} is already borrowed at, so it cannot be borrowed mutably. Previous borrows:\n[{2}]\n\n",
|
||||
.source.attempted_at,
|
||||
.type_name,
|
||||
locations_display(&.source.already_borrowed_at)
|
||||
)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(debug_assertions),
|
||||
error("{type_name} is already borrowed, so it cannot be borrowed mutably. (More detail available in debug mode)")
|
||||
)]
|
||||
AlreadyBorrowed {
|
||||
source: super::BorrowMutError,
|
||||
type_name: &'static str,
|
||||
},
|
||||
#[cfg_attr(
|
||||
debug_assertions,
|
||||
error(
|
||||
"[{0}] {1} is already borrowed mutably at [{2}], so it cannot be borrowed anymore.",
|
||||
.source.attempted_at,
|
||||
.type_name,
|
||||
locations_display(&.source.already_borrowed_at)
|
||||
)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
not(debug_assertions),
|
||||
error("{type_name} is already borrowed mutably, so it cannot be borrowed anymore. (More detail available in debug mode)")
|
||||
)]
|
||||
AlreadyBorrowedMutably {
|
||||
source: super::BorrowError,
|
||||
type_name: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
pub type UseSharedStateResult<T> = Result<T, UseSharedStateError>;
|
||||
}
|
||||
|
||||
type ProvidedState<T> = Rc<RefCell<ProvidedStateInner<T>>>;
|
||||
|
||||
// Tracks all the subscribers to a shared State
|
||||
@@ -95,7 +163,7 @@ pub fn use_shared_state<T: 'static>(cx: &ScopeState) -> Option<&UseSharedState<T
|
||||
|
||||
root.borrow_mut().consumers.insert(scope_id);
|
||||
|
||||
let state = UseSharedState { inner: root };
|
||||
let state = UseSharedState::new(root);
|
||||
let owner = UseSharedStateOwner { state, scope_id };
|
||||
Some(owner)
|
||||
});
|
||||
@@ -122,29 +190,97 @@ pub struct UseSharedState<T> {
|
||||
}
|
||||
|
||||
impl<T> UseSharedState<T> {
|
||||
fn new(inner: Rc<RefCell<ProvidedStateInner<T>>>) -> Self {
|
||||
Self { inner }
|
||||
}
|
||||
|
||||
/// 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
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
#[cfg_attr(debug_assertions, inline(never))]
|
||||
pub fn try_read(&self) -> UseSharedStateResult<Ref<'_, T>> {
|
||||
match self.inner.try_borrow() {
|
||||
Ok(value) => Ok(Ref::map(value, |inner| &inner.value)),
|
||||
Err(source) => Err(UseSharedStateError::AlreadyBorrowedMutably {
|
||||
source,
|
||||
type_name: std::any::type_name::<Self>(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Read the shared value
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
#[cfg_attr(debug_assertions, inline(never))]
|
||||
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
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
#[cfg_attr(debug_assertions, inline(never))]
|
||||
pub fn try_write(&self) -> UseSharedStateResult<RefMut<'_, T>> {
|
||||
match self.inner.try_borrow_mut() {
|
||||
Ok(mut value) => {
|
||||
value.notify_consumers();
|
||||
Ok(RefMut::map(value, |inner| &mut inner.value))
|
||||
}
|
||||
Err(source) => Err(UseSharedStateError::AlreadyBorrowed {
|
||||
source,
|
||||
type_name: std::any::type_name::<Self>(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
#[cfg_attr(debug_assertions, inline(never))]
|
||||
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
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
#[cfg_attr(debug_assertions, inline(never))]
|
||||
pub fn try_write_silent(&self) -> UseSharedStateResult<RefMut<'_, T>> {
|
||||
match self.inner.try_borrow_mut() {
|
||||
Ok(value) => Ok(RefMut::map(value, |inner| &mut inner.value)),
|
||||
Err(source) => Err(UseSharedStateError::AlreadyBorrowed {
|
||||
source,
|
||||
type_name: std::any::type_name::<Self>(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Writes the value without forcing a re-render
|
||||
#[cfg_attr(debug_assertions, track_caller)]
|
||||
#[cfg_attr(debug_assertions, inline(never))]
|
||||
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
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ description = "Build server-side apps with Dioxus"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
thiserror = "1.0.38"
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
slab = { workspace = true }
|
||||
futures-util = { workspace = true, default-features = false, features = [
|
||||
|
||||
@@ -14,6 +14,9 @@ dioxus = { workspace = true }
|
||||
dioxus-router-macro = { workspace = true }
|
||||
gloo = { version = "0.8.0", optional = true }
|
||||
log = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
serde_urlencoded = { version = "0.7.1", optional = true }
|
||||
serde = { version = "1", features = ["derive"], optional = true }
|
||||
thiserror = "1.0.37"
|
||||
url = "2.3.1"
|
||||
|
||||
@@ -21,6 +21,8 @@ tokio = { version = "1.28", features = ["full"] }
|
||||
|
||||
[dev-dependencies]
|
||||
dioxus = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
log = { workspace = true }
|
||||
fern = { version = "0.6.0", features = ["colored"] }
|
||||
anyhow = "1.0"
|
||||
argh = "0.1.4"
|
||||
|
||||
Reference in new issue
Block a user