clean up ref mapping with GATs

This commit is contained in:
Evan Almloff 2023-11-02 19:35:53 -05:00
parent 90e04edcdf
commit 0a8de1d40f
6 changed files with 245 additions and 150 deletions

View file

@ -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 {

View file

@ -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,
}

View file

@ -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())
}
}

View file

@ -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

View file

@ -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(),

View file

@ -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(&current_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