diff --git a/dynatos-reactive/src/derived.rs b/dynatos-reactive/src/derived.rs index f2cd26f..87e99ce 100644 --- a/dynatos-reactive/src/derived.rs +++ b/dynatos-reactive/src/derived.rs @@ -30,71 +30,99 @@ //! not only store the latest value, it also needs to create an effect that is re-run //! each time any dependencies are updated. -// TODO: Make `Derived` always `usize`-sized -// by merging the `effect::Inner` and `signal::Inner` somehow? - // Imports use { crate::{Effect, Signal, SignalSet, SignalWith}, - std::fmt, + std::{fmt, marker::Unsize, ops::CoerceUnsized}, }; /// Derived signal. /// /// See the module documentation for more information. -pub struct Derived { +pub struct Derived { /// Effect - effect: Effect, - - /// Value - value: Signal>, + effect: Effect>, } -impl Derived { +impl Derived { /// Creates a new derived signal - pub fn new(f: F) -> Self + pub fn new(f: F) -> Self where T: 'static, F: Fn() -> T + 'static, { let value = Signal::new(None); - let effect = Effect::new({ - let value = value.clone(); - move || value.set(Some(f())) - }); + let effect = Effect::new(EffectFn { value, f }); - Self { effect, value } + Self { effect } } } -impl SignalWith for Derived { +impl SignalWith for Derived { type Value = T; - fn with(&self, f: F) -> O + fn with(&self, f: F2) -> O where - F: FnOnce(&Self::Value) -> O, + F2: FnOnce(&Self::Value) -> O, { - self.value.with(|value| { + let effect_fn = self.effect.inner_fn(); + effect_fn.value.with(|value| { let value = value.as_ref().expect("Value wasn't initialized"); f(value) }) } } -impl Clone for Derived { +impl Clone for Derived { fn clone(&self) -> Self { Self { effect: self.effect.clone(), - value: self.value.clone(), } } } -impl fmt::Debug for Derived { +impl fmt::Debug for Derived { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Derived") - .field("effect", &self.effect) - .field("value", &self.value) - .finish() + let effect_fn = self.effect.inner_fn(); + f.debug_struct("Derived").field("value", &effect_fn.value).finish() + } +} + +impl CoerceUnsized> for Derived where F1: Unsize {} + +/// Effect function +struct EffectFn { + /// Value + // TODO: Remove the indirection of the inner signal here. + value: Signal>, + + /// Function + f: F, +} + +impl FnOnce<()> for EffectFn +where + F: Fn() -> T, +{ + type Output = (); + + extern "rust-call" fn call_once(mut self, args: ()) -> Self::Output { + self.call_mut(args) + } +} +impl FnMut<()> for EffectFn +where + F: Fn() -> T, +{ + extern "rust-call" fn call_mut(&mut self, args: ()) -> Self::Output { + self.call(args) + } +} +impl Fn<()> for EffectFn +where + F: Fn() -> T, +{ + extern "rust-call" fn call(&self, _args: ()) -> Self::Output { + self.value.set(Some((self.f)())); } } diff --git a/dynatos-reactive/src/lib.rs b/dynatos-reactive/src/lib.rs index c3e0051..8a848be 100644 --- a/dynatos-reactive/src/lib.rs +++ b/dynatos-reactive/src/lib.rs @@ -1,7 +1,7 @@ //! Reactivity for `dynatos` // Features -#![feature(unsize, coerce_unsized)] +#![feature(unsize, coerce_unsized, unboxed_closures, fn_traits)] // Modules pub mod derived; diff --git a/dynatos/src/element_dyn_attr.rs b/dynatos/src/element_dyn_attr.rs index 69cf903..c533ee4 100644 --- a/dynatos/src/element_dyn_attr.rs +++ b/dynatos/src/element_dyn_attr.rs @@ -142,12 +142,7 @@ where } // TODO: Allow impl for `impl SignalGet` -#[duplicate::duplicate_item( - Sig; - [Signal]; - [Derived]; -)] -impl WithDynAttr for Sig +impl WithDynAttr for Signal where T: WithDynAttr, { @@ -159,6 +154,19 @@ where } } +impl WithDynAttr for Derived +where + T: WithDynAttr, + F: ?Sized, +{ + fn with_attr(&self, f: F2) -> O + where + F2: FnOnce(Option<&str>) -> O, + { + self.with(|text| text.with_attr(f)) + } +} + /// Trait for values accepted by [`ElementDynAttr::set_dyn_attr_if`]. /// /// This allows it to work with the following types: @@ -188,12 +196,16 @@ impl DynAttrPred for bool { } // TODO: Allow impl for `impl SignalGet` -#[duplicate::duplicate_item( - Sig; - [Signal]; - [Derived]; -)] -impl DynAttrPred for Sig +impl DynAttrPred for Signal +where + T: DynAttrPred, +{ + fn eval(&self) -> bool { + self.with(T::eval) + } +} + +impl DynAttrPred for Derived where T: DynAttrPred, { diff --git a/dynatos/src/node_dyn_text.rs b/dynatos/src/node_dyn_text.rs index d7a57e8..536871e 100644 --- a/dynatos/src/node_dyn_text.rs +++ b/dynatos/src/node_dyn_text.rs @@ -113,12 +113,7 @@ where } // TODO: Allow impl for `impl SignalGet` -#[duplicate::duplicate_item( - Sig; - [Signal]; - [Derived]; -)] -impl WithDynText for Sig +impl WithDynText for Signal where T: WithDynText, { @@ -129,6 +124,18 @@ where self.with(|text| text.with_text(f)) } } +impl WithDynText for Derived +where + T: WithDynText, + F: ?Sized, +{ + fn with_text(&self, f: F2) -> O + where + F2: FnOnce(Option<&str>) -> O, + { + self.with(|text| text.with_text(f)) + } +} impl WithDynText for QuerySignal where T: WithDynText, diff --git a/dynatos/src/object_dyn_prop.rs b/dynatos/src/object_dyn_prop.rs index 9e40634..59a0f29 100644 --- a/dynatos/src/object_dyn_prop.rs +++ b/dynatos/src/object_dyn_prop.rs @@ -140,12 +140,7 @@ impl ToDynProp for Ty { } // TODO: Allow impl for `impl SignalGet` -#[duplicate::duplicate_item( - Sig; - [Signal]; - [Derived]; -)] -impl ToDynProp for Sig +impl ToDynProp for Signal where T: ToDynProp, { @@ -153,6 +148,15 @@ where self.with(|prop| prop.to_prop()) } } +impl ToDynProp for Derived +where + T: ToDynProp, + F: ?Sized, +{ + fn to_prop(&self) -> Option { + self.with(|prop| prop.to_prop()) + } +} impl ToDynProp for QuerySignal where T: ToDynProp,