mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-23 04:33:06 +00:00
clean up ref mapping with GATs
This commit is contained in:
parent
90e04edcdf
commit
0a8de1d40f
6 changed files with 245 additions and 150 deletions
|
@ -247,53 +247,82 @@ impl Default for SyncStorage {
|
|||
}
|
||||
}
|
||||
|
||||
pub trait Mappable<T, U>: Deref<Target = T> {
|
||||
type Mapped: Deref<Target = U>;
|
||||
pub trait Mappable<T>: Deref<Target = T> {
|
||||
type Mapped<U: 'static>: Mappable<U> + Deref<Target = U>;
|
||||
|
||||
fn map(_self: Self, f: fn(&T) -> &U) -> Self::Mapped;
|
||||
fn map<U: 'static>(_self: Self, f: fn(&T) -> &U) -> Self::Mapped<U>;
|
||||
|
||||
fn try_map<U: 'static>(_self: Self, f: fn(&T) -> Option<&U>) -> Option<Self::Mapped<U>>;
|
||||
}
|
||||
|
||||
impl<T, U: 'static> Mappable<T, U> for Ref<'static, T> {
|
||||
type Mapped = Ref<'static, U>;
|
||||
impl<T> Mappable<T> for Ref<'static, T> {
|
||||
type Mapped<U: 'static> = Ref<'static, U>;
|
||||
|
||||
fn map(_self: Self, f: fn(&T) -> &U) -> Self::Mapped {
|
||||
fn map<U: 'static>(_self: Self, f: fn(&T) -> &U) -> Self::Mapped<U> {
|
||||
Ref::map(_self, f)
|
||||
}
|
||||
|
||||
fn try_map<U: 'static>(_self: Self, f: fn(&T) -> Option<&U>) -> Option<Self::Mapped<U>> {
|
||||
Ref::try_map(_self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: 'static> Mappable<T, U> for MappedRwLockReadGuard<'static, T> {
|
||||
type Mapped = MappedRwLockReadGuard<'static, U>;
|
||||
impl<T> Mappable<T> for MappedRwLockReadGuard<'static, T> {
|
||||
type Mapped<U: 'static> = MappedRwLockReadGuard<'static, U>;
|
||||
|
||||
fn map(_self: Self, f: fn(&T) -> &U) -> Self::Mapped {
|
||||
fn map<U: 'static>(_self: Self, f: fn(&T) -> &U) -> Self::Mapped<U> {
|
||||
MappedRwLockReadGuard::map(_self, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait MappableMut<T, U>: DerefMut<Target = T> {
|
||||
type Mapped: DerefMut<Target = U>;
|
||||
|
||||
fn map(_self: Self, f: fn(&mut T) -> &mut U) -> Self::Mapped;
|
||||
}
|
||||
|
||||
impl<T, U: 'static> MappableMut<T, U> for RefMut<'static, T> {
|
||||
type Mapped = RefMut<'static, U>;
|
||||
|
||||
fn map(_self: Self, f: fn(&mut T) -> &mut U) -> Self::Mapped {
|
||||
RefMut::map(_self, f)
|
||||
fn try_map<U: 'static>(_self: Self, f: fn(&T) -> Option<&U>) -> Option<Self::Mapped<U>> {
|
||||
MappedRwLockReadGuard::try_map(_self, f).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, U: 'static> MappableMut<T, U> for MappedRwLockWriteGuard<'static, T> {
|
||||
type Mapped = MappedRwLockWriteGuard<'static, U>;
|
||||
pub trait MappableMut<T>: DerefMut<Target = T> {
|
||||
type Mapped<U: 'static>: MappableMut<U> + DerefMut<Target = U>;
|
||||
|
||||
fn map(_self: Self, f: fn(&mut T) -> &mut U) -> Self::Mapped {
|
||||
fn map<U: 'static>(_self: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped<U>;
|
||||
|
||||
fn try_map<U: 'static>(
|
||||
_self: Self,
|
||||
f: impl FnOnce(&mut T) -> Option<&mut U>,
|
||||
) -> Option<Self::Mapped<U>>;
|
||||
}
|
||||
|
||||
impl<T> MappableMut<T> for RefMut<'static, T> {
|
||||
type Mapped<U: 'static> = RefMut<'static, U>;
|
||||
|
||||
fn map<U: 'static>(_self: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped<U> {
|
||||
RefMut::map(_self, f)
|
||||
}
|
||||
|
||||
fn try_map<U: 'static>(
|
||||
_self: Self,
|
||||
f: impl FnOnce(&mut T) -> Option<&mut U>,
|
||||
) -> Option<Self::Mapped<U>> {
|
||||
RefMut::try_map(_self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> MappableMut<T> for MappedRwLockWriteGuard<'static, T> {
|
||||
type Mapped<U: 'static> = MappedRwLockWriteGuard<'static, U>;
|
||||
|
||||
fn map<U: 'static>(_self: Self, f: impl FnOnce(&mut T) -> &mut U) -> Self::Mapped<U> {
|
||||
MappedRwLockWriteGuard::map(_self, f)
|
||||
}
|
||||
|
||||
fn try_map<U: 'static>(
|
||||
_self: Self,
|
||||
f: impl FnOnce(&mut T) -> Option<&mut U>,
|
||||
) -> Option<Self::Mapped<U>> {
|
||||
MappedRwLockWriteGuard::try_map(_self, f).ok()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Storage<Data>: Copy + AnyStorage {
|
||||
type Ref: Deref<Target = Data>;
|
||||
type Mut: DerefMut<Target = Data>;
|
||||
type Ref: Mappable<Data> + Deref<Target = Data>;
|
||||
type Mut: MappableMut<Data> + DerefMut<Target = Data>;
|
||||
|
||||
fn try_read(&self) -> Option<Self::Ref>;
|
||||
fn read(&self) -> Self::Ref {
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
use core::{self, fmt::Debug};
|
||||
use std::fmt::{self, Formatter};
|
||||
//
|
||||
use dioxus_core::prelude::*;
|
||||
use generational_box::SyncStorage;
|
||||
use generational_box::UnsyncStorage;
|
||||
use std::fmt::{self, Formatter};
|
||||
|
||||
use crate::use_signal;
|
||||
use crate::{dependency::Dependency, CopyValue};
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub(crate) struct EffectStack {
|
||||
pub(crate) effects: CopyValue<Vec<Effect>, SyncStorage>,
|
||||
pub(crate) effects: CopyValue<Vec<Effect>, UnsyncStorage>,
|
||||
}
|
||||
|
||||
impl Default for EffectStack {
|
||||
|
@ -69,7 +68,7 @@ pub fn use_effect_with_dependencies<D: Dependency>(
|
|||
#[derive(Copy, Clone, PartialEq)]
|
||||
pub struct Effect {
|
||||
pub(crate) source: ScopeId,
|
||||
pub(crate) callback: CopyValue<Box<dyn FnMut()>, SyncStorage>,
|
||||
pub(crate) callback: CopyValue<Box<dyn FnMut()>, UnsyncStorage>,
|
||||
pub(crate) effect_stack: EffectStack,
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::rt::CopyValue;
|
||||
use crate::signal::{ReadOnlySignal, Signal, Write};
|
||||
use crate::SignalData;
|
||||
use generational_box::Mappable;
|
||||
use generational_box::{MappableMut, Storage};
|
||||
use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
|
||||
|
||||
|
@ -11,40 +12,40 @@ use std::{
|
|||
|
||||
macro_rules! read_impls {
|
||||
($ty:ident) => {
|
||||
impl<T: Default + 'static> Default for $ty<T> {
|
||||
impl<T: Default + 'static, S: Storage<T>> Default for $ty<T, S> {
|
||||
fn default() -> Self {
|
||||
Self::new(Default::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::clone::Clone for $ty<T> {
|
||||
impl<T, S: Storage<T>> std::clone::Clone for $ty<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Copy for $ty<T> {}
|
||||
impl<T, S: Storage<T> + Copy> Copy for $ty<T, S> {}
|
||||
|
||||
impl<T: Display + 'static> Display for $ty<T> {
|
||||
impl<T: Display + 'static, S: Storage<T>> Display for $ty<T, S> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.with(|v| Display::fmt(v, f))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Debug + 'static> Debug for $ty<T> {
|
||||
impl<T: Debug + 'static, S: Storage<T>> Debug for $ty<T, S> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.with(|v| Debug::fmt(v, f))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> $ty<Vec<T>> {
|
||||
impl<T: 'static, S: Storage<T>> $ty<Vec<T>, S> {
|
||||
/// Read a value from the inner vector.
|
||||
pub fn get(&self, index: usize) -> Option<MappedRwLockReadGuard<'static, T>> {
|
||||
MappedRwLockReadGuard::try_map(self.read(), |v| v.get(index)).ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> $ty<Option<T>> {
|
||||
impl<T: 'static, S: Storage<T>> $ty<Option<T>, S> {
|
||||
/// Unwraps the inner value and clones it.
|
||||
pub fn unwrap(&self) -> T
|
||||
where
|
||||
|
@ -63,7 +64,7 @@ macro_rules! read_impls {
|
|||
|
||||
macro_rules! write_impls {
|
||||
($ty:ident) => {
|
||||
impl<T: Add<Output = T> + Copy + 'static> std::ops::Add<T> for $ty<T> {
|
||||
impl<T: Add<Output = T> + Copy + 'static, S: Storage<T>> std::ops::Add<T> for $ty<T, S> {
|
||||
type Output = T;
|
||||
|
||||
fn add(self, rhs: T) -> Self::Output {
|
||||
|
@ -71,19 +72,23 @@ macro_rules! write_impls {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Add<Output = T> + Copy + 'static> std::ops::AddAssign<T> for $ty<T> {
|
||||
impl<T: Add<Output = T> + Copy + 'static, S: Storage<T>> std::ops::AddAssign<T>
|
||||
for $ty<T, S>
|
||||
{
|
||||
fn add_assign(&mut self, rhs: T) {
|
||||
self.with_mut(|v| *v = *v + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sub<Output = T> + Copy + 'static> std::ops::SubAssign<T> for $ty<T> {
|
||||
impl<T: Sub<Output = T> + Copy + 'static, S: Storage<T>> std::ops::SubAssign<T>
|
||||
for $ty<T, S>
|
||||
{
|
||||
fn sub_assign(&mut self, rhs: T) {
|
||||
self.with_mut(|v| *v = *v - rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Sub<Output = T> + Copy + 'static> std::ops::Sub<T> for $ty<T> {
|
||||
impl<T: Sub<Output = T> + Copy + 'static, S: Storage<T>> std::ops::Sub<T> for $ty<T, S> {
|
||||
type Output = T;
|
||||
|
||||
fn sub(self, rhs: T) -> Self::Output {
|
||||
|
@ -91,13 +96,15 @@ macro_rules! write_impls {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Mul<Output = T> + Copy + 'static> std::ops::MulAssign<T> for $ty<T> {
|
||||
impl<T: Mul<Output = T> + Copy + 'static, S: Storage<T>> std::ops::MulAssign<T>
|
||||
for $ty<T, S>
|
||||
{
|
||||
fn mul_assign(&mut self, rhs: T) {
|
||||
self.with_mut(|v| *v = *v * rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Mul<Output = T> + Copy + 'static> std::ops::Mul<T> for $ty<T> {
|
||||
impl<T: Mul<Output = T> + Copy + 'static, S: Storage<T>> std::ops::Mul<T> for $ty<T, S> {
|
||||
type Output = T;
|
||||
|
||||
fn mul(self, rhs: T) -> Self::Output {
|
||||
|
@ -105,13 +112,15 @@ macro_rules! write_impls {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Div<Output = T> + Copy + 'static> std::ops::DivAssign<T> for $ty<T> {
|
||||
impl<T: Div<Output = T> + Copy + 'static, S: Storage<T>> std::ops::DivAssign<T>
|
||||
for $ty<T, S>
|
||||
{
|
||||
fn div_assign(&mut self, rhs: T) {
|
||||
self.with_mut(|v| *v = *v / rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Div<Output = T> + Copy + 'static> std::ops::Div<T> for $ty<T> {
|
||||
impl<T: Div<Output = T> + Copy + 'static, S: Storage<T>> std::ops::Div<T> for $ty<T, S> {
|
||||
type Output = T;
|
||||
|
||||
fn div(self, rhs: T) -> Self::Output {
|
||||
|
@ -119,7 +128,7 @@ macro_rules! write_impls {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> $ty<Vec<T>> {
|
||||
impl<T: 'static, S: Storage<T>> $ty<Vec<T>, S> {
|
||||
/// Pushes a new value to the end of the vector.
|
||||
pub fn push(&self, value: T) {
|
||||
self.with_mut(|v| v.push(value))
|
||||
|
@ -171,7 +180,7 @@ macro_rules! write_impls {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static> $ty<Option<T>> {
|
||||
impl<T: 'static, S: Storage<T>> $ty<Option<T>, S> {
|
||||
/// Takes the value out of the Option.
|
||||
pub fn take(&self) -> Option<T> {
|
||||
self.with_mut(|v| v.take())
|
||||
|
@ -183,7 +192,7 @@ macro_rules! write_impls {
|
|||
}
|
||||
|
||||
/// Gets the value out of the Option, or inserts the given value if the Option is empty.
|
||||
pub fn get_or_insert(&self, default: T) -> MappedRwLockReadGuard<'_, T> {
|
||||
pub fn get_or_insert(&self, default: T) -> S::Ref {
|
||||
self.get_or_insert_with(|| default)
|
||||
}
|
||||
|
||||
|
@ -191,25 +200,25 @@ macro_rules! write_impls {
|
|||
pub fn get_or_insert_with(
|
||||
&self,
|
||||
default: impl FnOnce() -> T,
|
||||
) -> MappedRwLockReadGuard<'_, T> {
|
||||
) -> <S::Ref as Mappable<T>>::Mapped<T> {
|
||||
let borrow = self.read();
|
||||
if borrow.is_none() {
|
||||
drop(borrow);
|
||||
self.with_mut(|v| *v = Some(default()));
|
||||
MappedRwLockReadGuard::map(self.read(), |v| v.as_ref().unwrap())
|
||||
S::Ref::map(self.read(), |v| v.as_ref().unwrap())
|
||||
} else {
|
||||
MappedRwLockReadGuard::map(borrow, |v| v.as_ref().unwrap())
|
||||
S::Ref::map(borrow, |v| v.as_ref().unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// read_impls!(CopyValue);
|
||||
// write_impls!(CopyValue);
|
||||
// read_impls!(Signal);
|
||||
// write_impls!(Signal);
|
||||
// read_impls!(ReadOnlySignal);
|
||||
read_impls!(CopyValue);
|
||||
write_impls!(CopyValue);
|
||||
read_impls!(Signal);
|
||||
write_impls!(Signal);
|
||||
read_impls!(ReadOnlySignal);
|
||||
|
||||
/// An iterator over the values of a `CopyValue<Vec<T>>`.
|
||||
pub struct CopyValueIterator<T: 'static, S: Storage<T>> {
|
||||
|
@ -240,23 +249,19 @@ impl<T: Clone + 'static, S: Storage<T>> IntoIterator for CopyValue<Vec<T>, S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, S: Storage<Vec<T>>> CopyValue<Vec<T>, S>
|
||||
where
|
||||
<S as Storage<Vec<T>>>::Mut: MappableMut<Vec<T>, T>,
|
||||
{
|
||||
impl<T: 'static, S: Storage<Vec<T>>> CopyValue<Vec<T>, S> {
|
||||
/// Write to an element in the inner vector.
|
||||
pub fn get_mut(&self, index: usize) -> Option<<S::Mut as MappableMut<Vec<T>, T>>::Mapped> {
|
||||
MappedRwLockWriteGuard::try_map(self.write(), |v| v.get_mut(index)).ok()
|
||||
pub fn get_mut(&self, index: usize) -> Option<<S::Mut as MappableMut<Vec<T>>>::Mapped<T>> {
|
||||
S::Mut::try_map(self.write(), |v: &mut Vec<T>| v.get_mut(index))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, S: Storage<Option<T>>> CopyValue<Option<T>, S>
|
||||
where
|
||||
<S as Storage<Option<T>>>::Mut: MappableMut<Option<T>, T>,
|
||||
{
|
||||
impl<T: 'static, S: Storage<Option<T>>> CopyValue<Option<T>, S> {
|
||||
/// Deref the inner value mutably.
|
||||
pub fn as_mut(&self) -> Option<<S::Mut as MappableMut<Option<T>, T>>::Mapped> {
|
||||
S::Mut::map(self.try_write(), |v| v.as_mut()).ok()
|
||||
pub fn as_mut(
|
||||
&self,
|
||||
) -> Option<<<S as Storage<Option<T>>>::Mut as MappableMut<Option<T>>>::Mapped<T>> {
|
||||
S::Mut::try_map(self.write(), |v: &mut Option<T>| v.as_mut())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -291,20 +296,31 @@ impl<T: Clone + 'static, S: Storage<T>> IntoIterator for Signal<Vec<T>, S> {
|
|||
|
||||
impl<T: 'static, S: Storage<SignalData<Vec<T>>>> Signal<Vec<T>, S>
|
||||
where
|
||||
S::Mut: MappableMut<Vec<T>, T>,
|
||||
<<S as Storage<SignalData<std::vec::Vec<T>>>>::Mut as MappableMut<
|
||||
SignalData<std::vec::Vec<T>>,
|
||||
>>::Mapped<std::vec::Vec<T>>: MappableMut<std::vec::Vec<T>>,
|
||||
{
|
||||
/// Returns a reference to an element or `None` if out of bounds.
|
||||
pub fn get_mut(
|
||||
&self,
|
||||
index: usize,
|
||||
) -> Option<Write<T, <S::Mut as MappableMut<Vec<T>, T>>::Mapped, S, Vec<T>>> {
|
||||
S::Mut::map(self.write(), |v| v.get_mut(index))
|
||||
) -> Option<
|
||||
Write<
|
||||
T,
|
||||
<<<S as Storage<SignalData<Vec<T>>>>::Mut as MappableMut<SignalData<Vec<T>>>>::Mapped<
|
||||
Vec<T>,
|
||||
> as MappableMut<Vec<T>>>::Mapped<T>,
|
||||
S,
|
||||
Vec<T>,
|
||||
>,
|
||||
> {
|
||||
Write::filter_map(self.write(), |v| v.get_mut(index))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, S: Storage<SignalData<Option<T>>>> Signal<Option<T>, S> {
|
||||
/// Returns a reference to an element or `None` if out of bounds.
|
||||
pub fn as_mut(&self) -> Option<Write<SignalData<Option<T>>, S::Mut, S, Option<T>>> {
|
||||
pub fn as_mut(&self) -> Option<Write<T, <<<S as Storage<SignalData<Option<T>>>>::Mut as MappableMut<SignalData<Option<T>>>>::Mapped<Option<T>> as MappableMut<Option<T>>>::Mapped<T>, S, Option<T>>>{
|
||||
Write::filter_map(self.write(), |v| v.as_mut())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,32 +11,34 @@ use generational_box::{GenerationalBox, Owner};
|
|||
|
||||
use crate::Effect;
|
||||
|
||||
fn current_owner() -> Rc<Owner> {
|
||||
match Effect::current() {
|
||||
// If we are inside of an effect, we should use the owner of the effect as the owner of the value.
|
||||
Some(effect) => {
|
||||
let scope_id = effect.source;
|
||||
owner_in_scope(scope_id)
|
||||
}
|
||||
// Otherwise either get an owner from the current scope or create a new one.
|
||||
None => match has_context() {
|
||||
Some(rt) => rt,
|
||||
None => {
|
||||
let owner = Rc::new(current_store().owner());
|
||||
provide_context(owner).expect("in a virtual dom")
|
||||
}
|
||||
},
|
||||
}
|
||||
fn current_owner<S: Storage<T>, T>() -> Rc<Owner<S>> {
|
||||
todo!()
|
||||
// match Effect::current() {
|
||||
// // If we are inside of an effect, we should use the owner of the effect as the owner of the value.
|
||||
// Some(effect) => {
|
||||
// let scope_id = effect.source;
|
||||
// owner_in_scope(scope_id)
|
||||
// }
|
||||
// // Otherwise either get an owner from the current scope or create a new one.
|
||||
// None => match has_context() {
|
||||
// Some(rt) => rt,
|
||||
// None => {
|
||||
// let owner = Rc::new(current_store().owner());
|
||||
// provide_context(owner).expect("in a virtual dom")
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
}
|
||||
|
||||
fn owner_in_scope(scope: ScopeId) -> Rc<Owner> {
|
||||
match consume_context_from_scope(scope) {
|
||||
Some(rt) => rt,
|
||||
None => {
|
||||
let owner = Rc::new(current_store().owner());
|
||||
provide_context_to_scope(scope, owner).expect("in a virtual dom")
|
||||
}
|
||||
}
|
||||
fn owner_in_scope<S: Storage<T>, T>(scope: ScopeId) -> Rc<Owner<S>> {
|
||||
todo!()
|
||||
// match consume_context_from_scope(scope) {
|
||||
// Some(rt) => rt,
|
||||
// None => {
|
||||
// let owner = Rc::new(current_store().owner());
|
||||
// provide_context_to_scope(scope, owner).expect("in a virtual dom")
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
@ -48,13 +50,6 @@ pub struct CopyValue<T: 'static, S: AnyStorage> {
|
|||
origin_scope: ScopeId,
|
||||
}
|
||||
|
||||
impl<T: 'static, S: AnyStorage + Copy> Copy for CopyValue<T, S> {}
|
||||
impl<T: 'static, S: AnyStorage + Clone> Clone for CopyValue<T, S> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<T: 'static> serde::Serialize for CopyValue<T>
|
||||
where
|
||||
|
|
|
@ -75,7 +75,7 @@ where
|
|||
pub fn selector<R: PartialEq, S: Storage<SignalData<R>>>(
|
||||
mut f: impl FnMut() -> R + 'static,
|
||||
) -> ReadOnlySignal<R, S> {
|
||||
let state = Signal::<R> {
|
||||
let state = Signal::<R, S> {
|
||||
inner: CopyValue::invalid(),
|
||||
};
|
||||
let effect = Effect {
|
||||
|
@ -89,7 +89,6 @@ pub fn selector<R: PartialEq, S: Storage<SignalData<R>>>(
|
|||
}
|
||||
state.inner.value.set(SignalData {
|
||||
subscribers: Default::default(),
|
||||
effect_subscribers: Default::default(),
|
||||
update_any: schedule_update_any().expect("in a virtual dom"),
|
||||
value: f(),
|
||||
effect_stack: get_effect_stack(),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
marker::PhantomData,
|
||||
mem::MaybeUninit,
|
||||
ops::{Deref, DerefMut},
|
||||
rc::Rc,
|
||||
|
@ -10,8 +11,8 @@ use dioxus_core::{
|
|||
prelude::{current_scope_id, has_context, provide_context, schedule_update_any},
|
||||
ScopeId, ScopeState,
|
||||
};
|
||||
use generational_box::{AnyStorage, Mappable, MappableMut, Storage};
|
||||
use parking_lot::{MappedRwLockReadGuard, MappedRwLockWriteGuard};
|
||||
use generational_box::{AnyStorage, Mappable, MappableMut, Storage, SyncStorage, UnsyncStorage};
|
||||
use parking_lot::RwLock;
|
||||
|
||||
use crate::{get_effect_stack, CopyValue, Effect, EffectStack};
|
||||
|
||||
|
@ -47,11 +48,51 @@ use crate::{get_effect_stack, CopyValue, Effect, EffectStack};
|
|||
/// }
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn use_signal<T: 'static, S: Storage<SignalData<T>>>(
|
||||
pub fn use_signal<T: 'static>(cx: &ScopeState, f: impl FnOnce() -> T) -> Signal<T, UnsyncStorage> {
|
||||
*cx.use_hook(|| Signal::new(f()))
|
||||
}
|
||||
|
||||
/// Creates a new `Send + Sync`` Signal. Signals are a Copy state management solution with automatic dependency tracking.
|
||||
///
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
/// use dioxus_signals::*;
|
||||
///
|
||||
/// fn App(cx: Scope) -> Element {
|
||||
/// let mut count = use_signal(cx, || 0);
|
||||
///
|
||||
/// // Because signals have automatic dependency tracking, if you never read them in a component, that component will not be re-rended when the signal is updated.
|
||||
/// // The app component will never be rerendered in this example.
|
||||
/// render! { Child { state: count } }
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn Child(cx: Scope, state: Signal<u32>) -> Element {
|
||||
/// let state = *state;
|
||||
///
|
||||
/// use_future!(cx, |()| async move {
|
||||
/// // This signal is Send + Sync, so we can use it in an another thread
|
||||
/// tokio::spawn(async move {
|
||||
/// // Because the signal is a Copy type, we can use it in an async block without cloning it.
|
||||
/// *state.write() += 1;
|
||||
/// }).await;
|
||||
/// });
|
||||
///
|
||||
/// render! {
|
||||
/// button {
|
||||
/// onclick: move |_| *state.write() += 1,
|
||||
/// "{state}"
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[must_use]
|
||||
pub fn use_signal_sync<T: Send + Sync + 'static>(
|
||||
cx: &ScopeState,
|
||||
f: impl FnOnce() -> T,
|
||||
) -> Signal<T, S> {
|
||||
*cx.use_hook(|| Signal::new(f()))
|
||||
) -> Signal<T, SyncStorage> {
|
||||
// *cx.use_hook(|| Signal::new(f()))
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -83,10 +124,15 @@ fn current_unsubscriber() -> Unsubscriber {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct SignalSubscribers {
|
||||
pub(crate) subscribers: Vec<ScopeId>,
|
||||
pub(crate) effect_subscribers: Vec<Effect>,
|
||||
}
|
||||
|
||||
pub(crate) struct SignalData<T> {
|
||||
pub(crate) subscribers: Rc<RefCell<Vec<ScopeId>>>,
|
||||
pub(crate) effect_subscribers: Rc<RefCell<Vec<Effect>>>,
|
||||
pub(crate) update_any: Arc<dyn Fn(ScopeId)>,
|
||||
pub(crate) subscribers: Arc<RwLock<SignalSubscribers>>,
|
||||
pub(crate) update_any: Arc<dyn Fn(ScopeId) + Sync + Send>,
|
||||
pub(crate) effect_stack: EffectStack,
|
||||
pub(crate) value: T,
|
||||
}
|
||||
|
@ -123,7 +169,7 @@ pub(crate) struct SignalData<T> {
|
|||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Signal<T: 'static, S: AnyStorage> {
|
||||
pub struct Signal<T: 'static, S: Storage<SignalData<T>>> {
|
||||
pub(crate) inner: CopyValue<SignalData<T>, S>,
|
||||
}
|
||||
|
||||
|
@ -147,7 +193,6 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
|
|||
Self {
|
||||
inner: CopyValue::new(SignalData {
|
||||
subscribers: Default::default(),
|
||||
effect_subscribers: Default::default(),
|
||||
update_any: schedule_update_any().expect("in a virtual dom"),
|
||||
value,
|
||||
effect_stack: get_effect_stack(),
|
||||
|
@ -161,7 +206,6 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
|
|||
inner: CopyValue::new_in_scope(
|
||||
SignalData {
|
||||
subscribers: Default::default(),
|
||||
effect_subscribers: Default::default(),
|
||||
update_any: schedule_update_any().expect("in a virtual dom"),
|
||||
value,
|
||||
effect_stack: get_effect_stack(),
|
||||
|
@ -178,13 +222,13 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
|
|||
|
||||
/// Get the current value of the signal. This will subscribe the current scope to the signal.
|
||||
/// If the signal has been dropped, this will panic.
|
||||
pub fn read(&self) -> <<S as Storage<SignalData<T>>>::Ref as Mappable<SignalData<T>, T>>::Mapped
|
||||
where
|
||||
<S as Storage<SignalData<T>>>::Ref: Mappable<SignalData<T>, T>,
|
||||
{
|
||||
pub fn read(
|
||||
&self,
|
||||
) -> <<S as Storage<SignalData<T>>>::Ref as Mappable<SignalData<T>>>::Mapped<T> {
|
||||
let inner = self.inner.read();
|
||||
if let Some(effect) = inner.effect_stack.current() {
|
||||
let mut effect_subscribers = inner.effect_subscribers.borrow_mut();
|
||||
let mut subscribers = inner.subscribers.write();
|
||||
let mut effect_subscribers = &mut subscribers.effect_subscribers;
|
||||
if !effect_subscribers.contains(&effect) {
|
||||
effect_subscribers.push(effect);
|
||||
}
|
||||
|
@ -196,12 +240,17 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
|
|||
self.inner.value,
|
||||
current_scope_id
|
||||
);
|
||||
let mut subscribers = inner.subscribers.borrow_mut();
|
||||
let mut subscribers = inner.subscribers.write();
|
||||
let subscribers = &mut subscribers.subscribers;
|
||||
if !subscribers.contains(¤t_scope_id) {
|
||||
subscribers.push(current_scope_id);
|
||||
drop(subscribers);
|
||||
let unsubscriber = current_unsubscriber();
|
||||
inner.subscribers.borrow_mut().push(unsubscriber.scope);
|
||||
inner
|
||||
.subscribers
|
||||
.write()
|
||||
.subscribers
|
||||
.push(unsubscriber.scope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,22 +261,21 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
|
|||
/// If the signal has been dropped, this will panic.
|
||||
pub fn write(
|
||||
&self,
|
||||
) -> Write<T, <<S as Storage<SignalData<T>>>::Mut as MappableMut<SignalData<T>, T>>::Mapped, S>
|
||||
where
|
||||
<S as Storage<SignalData<T>>>::Mut: MappableMut<SignalData<T>, T>,
|
||||
) -> Write<T, <<S as Storage<SignalData<T>>>::Mut as MappableMut<SignalData<T>>>::Mapped<T>, S>
|
||||
{
|
||||
let inner = self.inner.write();
|
||||
let borrow = S::Mut::map(inner, |v| &mut v.value);
|
||||
Write {
|
||||
write: borrow,
|
||||
signal: SignalSubscriberDrop { signal: *self },
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_subscribers(&self) {
|
||||
{
|
||||
let inner = self.inner.read();
|
||||
for &scope_id in &*inner.subscribers.borrow() {
|
||||
for &scope_id in &*inner.subscribers.read().subscribers {
|
||||
tracing::trace!(
|
||||
"Write on {:?} triggered update on {:?}",
|
||||
self.inner.value,
|
||||
|
@ -239,7 +287,7 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
|
|||
|
||||
let subscribers = {
|
||||
let self_read = self.inner.read();
|
||||
let mut effects = self_read.effect_subscribers.borrow_mut();
|
||||
let mut effects = &mut self_read.subscribers.write().effect_subscribers;
|
||||
std::mem::take(&mut *effects)
|
||||
};
|
||||
for effect in subscribers {
|
||||
|
@ -280,7 +328,7 @@ impl<T: Clone + 'static, S: Storage<SignalData<T>>> Signal<T, S> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<S: Storage<bool>> Signal<bool, S> {
|
||||
impl<S: Storage<SignalData<bool>>> Signal<bool, S> {
|
||||
/// Invert the boolean value of the signal. This will trigger an update on all subscribers.
|
||||
pub fn toggle(&self) {
|
||||
self.set(!self.value());
|
||||
|
@ -294,7 +342,8 @@ impl<T: 'static, S: Storage<SignalData<T>>> PartialEq for Signal<T, S> {
|
|||
}
|
||||
|
||||
impl<T, S: Storage<SignalData<T>>> Deref for Signal<T, S> {
|
||||
type Target = dyn Fn() -> S::Ref;
|
||||
type Target =
|
||||
dyn Fn() -> <<S as Storage<SignalData<T>>>::Ref as Mappable<SignalData<T>>>::Mapped<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// https://github.com/dtolnay/case-studies/tree/master/callable-types
|
||||
|
@ -337,21 +386,25 @@ impl<T: 'static, S: Storage<SignalData<T>>> Drop for SignalSubscriberDrop<T, S>
|
|||
}
|
||||
|
||||
/// A mutable reference to a signal's value.
|
||||
pub struct Write<T: 'static, B: DerefMut<Target = T>, S: Storage<SignalData<I>>, I: 'static = T> {
|
||||
///
|
||||
/// T is the current type of the write
|
||||
/// B is the dynamicly checked type of the write (RefMut)
|
||||
/// S is the storage type of the signal
|
||||
/// I is the type of the original signal
|
||||
pub struct Write<T: 'static, B: MappableMut<T>, S: Storage<SignalData<I>>, I: 'static = T> {
|
||||
write: B,
|
||||
signal: SignalSubscriberDrop<I, S>,
|
||||
phantom: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T: 'static, B: DerefMut<Target = T>, S: Storage<SignalData<I>>, I: 'static> Write<T, B, S, I> {
|
||||
impl<T: 'static, B: MappableMut<T>, S: Storage<SignalData<I>>, I: 'static> Write<T, B, S, I> {
|
||||
/// Map the mutable reference to the signal's value to a new type.
|
||||
pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<O, S::Mapped, S, I>
|
||||
where
|
||||
S: MappableMut<T, O>,
|
||||
{
|
||||
let Self { write, signal } = myself;
|
||||
pub fn map<O>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<O, B::Mapped<O>, S, I> {
|
||||
let Self { write, signal, .. } = myself;
|
||||
Write {
|
||||
write: S::map(write, f),
|
||||
write: B::map(write, f),
|
||||
signal,
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -359,17 +412,18 @@ impl<T: 'static, B: DerefMut<Target = T>, S: Storage<SignalData<I>>, I: 'static>
|
|||
pub fn filter_map<O>(
|
||||
myself: Self,
|
||||
f: impl FnOnce(&mut T) -> Option<&mut O>,
|
||||
) -> Option<Write<O, S::Mapped, S, I>>
|
||||
where
|
||||
S: MappableMut<T, O>,
|
||||
{
|
||||
let Self { write, signal } = myself;
|
||||
let write = MappedRwLockWriteGuard::try_map(write, f).ok();
|
||||
write.map(|write| Write { write, signal })
|
||||
) -> Option<Write<O, B::Mapped<O>, S, I>> {
|
||||
let Self { write, signal, .. } = myself;
|
||||
let write = B::try_map(write, f);
|
||||
write.map(|write| Write {
|
||||
write,
|
||||
signal,
|
||||
phantom: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: 'static, B: DerefMut<Target = T>, S: Storage<SignalData<I>>, I: 'static> Deref
|
||||
impl<T: 'static, B: MappableMut<T>, S: Storage<SignalData<I>>, I: 'static> Deref
|
||||
for Write<T, B, S, I>
|
||||
{
|
||||
type Target = T;
|
||||
|
@ -379,7 +433,7 @@ impl<T: 'static, B: DerefMut<Target = T>, S: Storage<SignalData<I>>, I: 'static>
|
|||
}
|
||||
}
|
||||
|
||||
impl<T, B: DerefMut<Target = T>, S: Storage<SignalData<I>>, I> DerefMut for Write<T, B, S, I> {
|
||||
impl<T, B: MappableMut<T>, S: Storage<SignalData<I>>, I> DerefMut for Write<T, B, S, I> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.write
|
||||
}
|
||||
|
@ -402,7 +456,9 @@ impl<T: 'static, S: Storage<SignalData<T>>> ReadOnlySignal<T, S> {
|
|||
}
|
||||
|
||||
/// Get the current value of the signal. This will subscribe the current scope to the signal.
|
||||
pub fn read(&self) -> MappedRwLockReadGuard<'static, T> {
|
||||
pub fn read(
|
||||
&self,
|
||||
) -> <<S as Storage<SignalData<T>>>::Ref as Mappable<SignalData<T>>>::Mapped<T> {
|
||||
self.inner.read()
|
||||
}
|
||||
|
||||
|
@ -426,7 +482,8 @@ impl<T: 'static, S: Storage<SignalData<T>>> PartialEq for ReadOnlySignal<T, S> {
|
|||
}
|
||||
|
||||
impl<T, S: Storage<SignalData<T>>> Deref for ReadOnlySignal<T, S> {
|
||||
type Target = dyn Fn() -> S::Ref;
|
||||
type Target =
|
||||
dyn Fn() -> <<S as Storage<SignalData<T>>>::Ref as Mappable<SignalData<T>>>::Mapped<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
// https://github.com/dtolnay/case-studies/tree/master/callable-types
|
||||
|
|
Loading…
Reference in a new issue