diff --git a/Cargo.toml b/Cargo.toml index b01396a26..7bb7d1570 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ members = [ "packages/native-core", "packages/native-core-macro", "packages/rsx-rosetta", - "packages/generational_box", + "packages/generational-box", "packages/signals", "packages/hot-reload", "packages/fullstack", @@ -77,7 +77,7 @@ dioxus-native-core = { path = "packages/native-core", version = "0.4.0" } dioxus-native-core-macro = { path = "packages/native-core-macro", version = "0.4.0" } rsx-rosetta = { path = "packages/rsx-rosetta", version = "0.4.0" } dioxus-signals = { path = "packages/signals" } -generational-box = { path = "packages/generational_box" } +generational-box = { path = "packages/generational-box" } dioxus-hot-reload = { path = "packages/hot-reload", version = "0.4.0" } dioxus-fullstack = { path = "packages/fullstack", version = "0.4.1" } dioxus_server_macro = { path = "packages/server-macro", version = "0.4.1" } diff --git a/packages/generational_box/Cargo.toml b/packages/generational-box/Cargo.toml similarity index 100% rename from packages/generational_box/Cargo.toml rename to packages/generational-box/Cargo.toml diff --git a/packages/generational_box/src/lib.rs b/packages/generational-box/src/lib.rs similarity index 100% rename from packages/generational_box/src/lib.rs rename to packages/generational-box/src/lib.rs diff --git a/packages/signals/src/impls.rs b/packages/signals/src/impls.rs index 0ce739640..0328d4063 100644 --- a/packages/signals/src/impls.rs +++ b/packages/signals/src/impls.rs @@ -1,5 +1,5 @@ use crate::rt::CopyValue; -use crate::{Signal, Write}; +use crate::signal::{ReadOnlySignal, Signal, Write}; use std::cell::{Ref, RefMut}; @@ -8,11 +8,11 @@ use std::{ ops::{Add, Div, Mul, Sub}, }; -macro_rules! impls { +macro_rules! read_impls { ($ty:ident) => { impl Default for $ty { fn default() -> Self { - Self::new(T::default()) + Self::new(Default::default()) } } @@ -36,6 +36,29 @@ macro_rules! impls { } } + impl $ty> { + pub fn get(&self, index: usize) -> Option> { + Ref::filter_map(self.read(), |v| v.get(index)).ok() + } + } + + impl $ty> { + pub fn unwrap(&self) -> T + where + T: Clone, + { + self.with(|v| v.clone()).unwrap() + } + + pub fn as_ref(&self) -> Option> { + Ref::filter_map(self.read(), |v| v.as_ref()).ok() + } + } + }; +} + +macro_rules! write_impls { + ($ty:ident) => { impl + Copy + 'static> std::ops::AddAssign for $ty { fn add_assign(&mut self, rhs: T) { self.with_mut(|v| *v = *v + rhs) @@ -100,10 +123,6 @@ macro_rules! impls { pub fn split_off(&self, at: usize) -> Vec { self.with_mut(|v| v.split_off(at)) } - - pub fn get(&self, index: usize) -> Option> { - Ref::filter_map(self.read(), |v| v.get(index)).ok() - } } impl $ty> { @@ -115,17 +134,6 @@ macro_rules! impls { self.with_mut(|v| v.replace(value)) } - pub fn unwrap(&self) -> T - where - T: Clone, - { - self.with(|v| v.clone()).unwrap() - } - - pub fn as_ref(&self) -> Option> { - Ref::filter_map(self.read(), |v| v.as_ref()).ok() - } - pub fn get_or_insert(&self, default: T) -> Ref<'_, T> { self.get_or_insert_with(|| default) } @@ -144,8 +152,11 @@ macro_rules! impls { }; } -impls!(CopyValue); -impls!(Signal); +read_impls!(CopyValue); +write_impls!(CopyValue); +read_impls!(Signal); +write_impls!(Signal); +read_impls!(ReadOnlySignal); pub struct CopyValueIterator { index: usize, diff --git a/packages/signals/src/lib.rs b/packages/signals/src/lib.rs index 179e0bef8..66b7801f0 100644 --- a/packages/signals/src/lib.rs +++ b/packages/signals/src/lib.rs @@ -1,10 +1,3 @@ -use std::{ - cell::{Ref, RefCell, RefMut}, - ops::{Deref, DerefMut}, - rc::Rc, - sync::Arc, -}; - mod rt; pub use rt::*; mod effect; @@ -12,205 +5,5 @@ pub use effect::*; mod impls; mod memo; pub use memo::*; - -use dioxus_core::{ - prelude::{current_scope_id, has_context, provide_context, schedule_update_any}, - ScopeId, ScopeState, -}; - -pub fn use_signal(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal { - *cx.use_hook(|| Signal::new(f())) -} - -#[derive(Clone)] -struct Unsubscriber { - scope: ScopeId, - subscribers: Rc>>>>>, -} - -impl Drop for Unsubscriber { - fn drop(&mut self) { - for subscribers in self.subscribers.borrow().iter() { - subscribers.borrow_mut().retain(|s| *s != self.scope); - } - } -} - -fn current_unsubscriber() -> Unsubscriber { - match has_context() { - Some(rt) => rt, - None => { - let owner = Unsubscriber { - scope: current_scope_id().expect("in a virtual dom"), - subscribers: Default::default(), - }; - provide_context(owner).expect("in a virtual dom") - } - } -} - -struct SignalData { - subscribers: Rc>>, - effect_subscribers: Rc>>, - update_any: Arc, - pub(crate) value: T, -} - -pub struct Signal { - pub(crate) inner: CopyValue>, -} - -impl Signal { - pub fn new(value: T) -> Self { - Self { - inner: CopyValue::new(SignalData { - subscribers: Default::default(), - effect_subscribers: Default::default(), - update_any: schedule_update_any().expect("in a virtual dom"), - value, - }), - } - } - - pub fn origin_scope(&self) -> ScopeId { - self.inner.origin_scope() - } - - pub fn read(&self) -> Ref { - let inner = self.inner.read(); - if let Some(effect) = Effect::current() { - let mut effect_subscribers = inner.effect_subscribers.borrow_mut(); - if !effect_subscribers.contains(&effect) { - effect_subscribers.push(effect); - } - } else if let Some(current_scope_id) = current_scope_id() { - log::trace!( - "{:?} subscribed to {:?}", - self.inner.value, - current_scope_id - ); - let mut subscribers = inner.subscribers.borrow_mut(); - if !subscribers.contains(¤t_scope_id) { - subscribers.push(current_scope_id); - drop(subscribers); - let unsubscriber = current_unsubscriber(); - inner.subscribers.borrow_mut().push(unsubscriber.scope); - } - } - Ref::map(inner, |v| &v.value) - } - - pub fn write(&self) -> Write<'_, T> { - let inner = self.inner.write(); - let borrow = RefMut::map(inner, |v| &mut v.value); - Write { - write: borrow, - signal: SignalSubscriberDrop { signal: *self }, - } - } - - fn update_subscribers(&self) { - { - let inner = self.inner.read(); - for &scope_id in &*inner.subscribers.borrow() { - log::trace!( - "Write on {:?} triggered update on {:?}", - self.inner.value, - scope_id - ); - (inner.update_any)(scope_id); - } - } - - let subscribers = { - let self_read = self.inner.read(); - let mut effects = self_read.effect_subscribers.borrow_mut(); - std::mem::take(&mut *effects) - }; - for effect in subscribers { - log::trace!( - "Write on {:?} triggered effect {:?}", - self.inner.value, - effect - ); - effect.try_run(); - } - } - - pub fn set(&self, value: T) { - *self.write() = value; - } - - pub fn with(&self, f: impl FnOnce(&T) -> O) -> O { - let write = self.read(); - f(&*write) - } - - pub fn with_mut(&self, f: impl FnOnce(&mut T) -> O) -> O { - let mut write = self.write(); - f(&mut *write) - } -} - -impl Signal { - pub fn value(&self) -> T { - self.read().clone() - } -} - -impl PartialEq for Signal { - fn eq(&self, other: &Self) -> bool { - self.inner == other.inner - } -} - -struct SignalSubscriberDrop { - signal: Signal, -} - -impl Drop for SignalSubscriberDrop { - fn drop(&mut self) { - self.signal.update_subscribers(); - } -} - -pub struct Write<'a, T: 'static, I: 'static = T> { - write: RefMut<'a, T>, - signal: SignalSubscriberDrop, -} - -impl<'a, T: 'static, I: 'static> Write<'a, T, I> { - pub fn map(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> { - let Self { write, signal } = myself; - Write { - write: RefMut::map(write, f), - signal, - } - } - - pub fn filter_map( - myself: Self, - f: impl FnOnce(&mut T) -> Option<&mut O>, - ) -> Option> { - let Self { write, signal } = myself; - let write = RefMut::filter_map(write, f).ok(); - write.map(|write| Write { - write, - signal: signal, - }) - } -} - -impl<'a, T: 'static> Deref for Write<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &*self.write - } -} - -impl DerefMut for Write<'_, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut *self.write - } -} +pub(crate) mod signal; +pub use signal::*; diff --git a/packages/signals/src/memo.rs b/packages/signals/src/memo.rs index 07d18f5a6..62292731a 100644 --- a/packages/signals/src/memo.rs +++ b/packages/signals/src/memo.rs @@ -1,12 +1,15 @@ use dioxus_core::prelude::*; -use crate::{get_effect_stack, CopyValue, Effect, Signal, SignalData}; +use crate::{get_effect_stack, signal::SignalData, CopyValue, Effect, ReadOnlySignal, Signal}; -pub fn use_memo(cx: &ScopeState, f: impl FnMut() -> R + 'static) -> Signal { - *cx.use_hook(|| memo(f)) +pub fn use_selector( + cx: &ScopeState, + f: impl FnMut() -> R + 'static, +) -> ReadOnlySignal { + *cx.use_hook(|| selector(f)) } -pub fn memo(mut f: impl FnMut() -> R + 'static) -> Signal { +fn selector(mut f: impl FnMut() -> R + 'static) -> ReadOnlySignal { let state = Signal:: { inner: CopyValue::invalid(), }; @@ -38,5 +41,5 @@ pub fn memo(mut f: impl FnMut() -> R + 'static) -> Signal { } })); - state + ReadOnlySignal::new(state) } diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs new file mode 100644 index 000000000..0c45da75a --- /dev/null +++ b/packages/signals/src/signal.rs @@ -0,0 +1,244 @@ +use std::{ + cell::{Ref, RefCell, RefMut}, + ops::{Deref, DerefMut}, + rc::Rc, + sync::Arc, +}; + +use dioxus_core::{ + prelude::{current_scope_id, has_context, provide_context, schedule_update_any}, + ScopeId, ScopeState, +}; + +use crate::{CopyValue, Effect}; + +pub fn use_signal(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal { + *cx.use_hook(|| Signal::new(f())) +} + +#[derive(Clone)] +struct Unsubscriber { + scope: ScopeId, + subscribers: Rc>>>>>, +} + +impl Drop for Unsubscriber { + fn drop(&mut self) { + for subscribers in self.subscribers.borrow().iter() { + subscribers.borrow_mut().retain(|s| *s != self.scope); + } + } +} + +fn current_unsubscriber() -> Unsubscriber { + match has_context() { + Some(rt) => rt, + None => { + let owner = Unsubscriber { + scope: current_scope_id().expect("in a virtual dom"), + subscribers: Default::default(), + }; + provide_context(owner).expect("in a virtual dom") + } + } +} + +pub(crate) struct SignalData { + pub(crate) subscribers: Rc>>, + pub(crate) effect_subscribers: Rc>>, + pub(crate) update_any: Arc, + pub(crate) value: T, +} + +pub struct Signal { + pub(crate) inner: CopyValue>, +} + +impl Signal { + pub fn new(value: T) -> Self { + Self { + inner: CopyValue::new(SignalData { + subscribers: Default::default(), + effect_subscribers: Default::default(), + update_any: schedule_update_any().expect("in a virtual dom"), + value, + }), + } + } + + pub fn origin_scope(&self) -> ScopeId { + self.inner.origin_scope() + } + + pub fn read(&self) -> Ref { + let inner = self.inner.read(); + if let Some(effect) = Effect::current() { + let mut effect_subscribers = inner.effect_subscribers.borrow_mut(); + if !effect_subscribers.contains(&effect) { + effect_subscribers.push(effect); + } + } else if let Some(current_scope_id) = current_scope_id() { + log::trace!( + "{:?} subscribed to {:?}", + self.inner.value, + current_scope_id + ); + let mut subscribers = inner.subscribers.borrow_mut(); + if !subscribers.contains(¤t_scope_id) { + subscribers.push(current_scope_id); + drop(subscribers); + let unsubscriber = current_unsubscriber(); + inner.subscribers.borrow_mut().push(unsubscriber.scope); + } + } + Ref::map(inner, |v| &v.value) + } + + pub fn write(&self) -> Write<'_, T> { + let inner = self.inner.write(); + let borrow = RefMut::map(inner, |v| &mut v.value); + Write { + write: borrow, + signal: SignalSubscriberDrop { signal: *self }, + } + } + + fn update_subscribers(&self) { + { + let inner = self.inner.read(); + for &scope_id in &*inner.subscribers.borrow() { + log::trace!( + "Write on {:?} triggered update on {:?}", + self.inner.value, + scope_id + ); + (inner.update_any)(scope_id); + } + } + + let subscribers = { + let self_read = self.inner.read(); + let mut effects = self_read.effect_subscribers.borrow_mut(); + std::mem::take(&mut *effects) + }; + for effect in subscribers { + log::trace!( + "Write on {:?} triggered effect {:?}", + self.inner.value, + effect + ); + effect.try_run(); + } + } + + pub fn set(&self, value: T) { + *self.write() = value; + } + + pub fn with(&self, f: impl FnOnce(&T) -> O) -> O { + let write = self.read(); + f(&*write) + } + + pub fn with_mut(&self, f: impl FnOnce(&mut T) -> O) -> O { + let mut write = self.write(); + f(&mut *write) + } +} + +impl Signal { + pub fn value(&self) -> T { + self.read().clone() + } +} + +impl PartialEq for Signal { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } +} + +struct SignalSubscriberDrop { + signal: Signal, +} + +impl Drop for SignalSubscriberDrop { + fn drop(&mut self) { + self.signal.update_subscribers(); + } +} + +pub struct Write<'a, T: 'static, I: 'static = T> { + write: RefMut<'a, T>, + signal: SignalSubscriberDrop, +} + +impl<'a, T: 'static, I: 'static> Write<'a, T, I> { + pub fn map(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, I> { + let Self { write, signal } = myself; + Write { + write: RefMut::map(write, f), + signal, + } + } + + pub fn filter_map( + myself: Self, + f: impl FnOnce(&mut T) -> Option<&mut O>, + ) -> Option> { + let Self { write, signal } = myself; + let write = RefMut::filter_map(write, f).ok(); + write.map(|write| Write { + write, + signal: signal, + }) + } +} + +impl<'a, T: 'static> Deref for Write<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &*self.write + } +} + +impl DerefMut for Write<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut *self.write + } +} + +pub struct ReadOnlySignal { + inner: Signal, +} + +impl ReadOnlySignal { + pub fn new(signal: Signal) -> Self { + Self { inner: signal } + } + + pub fn origin_scope(&self) -> ScopeId { + self.inner.origin_scope() + } + + pub fn read(&self) -> Ref { + self.inner.read() + } + + pub fn with(&self, f: impl FnOnce(&T) -> O) -> O { + self.inner.with(f) + } +} + +impl ReadOnlySignal { + pub fn value(&self) -> T { + self.read().clone() + } +} + +impl PartialEq for ReadOnlySignal { + fn eq(&self, other: &Self) -> bool { + self.inner == other.inner + } +}