Derived now has a type parameter for the effect function.

This commit is contained in:
2024-02-28 17:19:19 +00:00
parent 53f2a26b64
commit 8b44ae1ec5
5 changed files with 103 additions and 52 deletions

View File

@@ -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<T> {
pub struct Derived<T, F: ?Sized> {
/// Effect
effect: Effect<dyn Fn()>,
/// Value
value: Signal<Option<T>>,
effect: Effect<EffectFn<T, F>>,
}
impl<T> Derived<T> {
impl<T, F> Derived<T, F> {
/// Creates a new derived signal
pub fn new<F>(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<T> SignalWith for Derived<T> {
impl<T, F: ?Sized> SignalWith for Derived<T, F> {
type Value = T;
fn with<F, O>(&self, f: F) -> O
fn with<F2, O>(&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<T> Clone for Derived<T> {
impl<T, F: ?Sized> Clone for Derived<T, F> {
fn clone(&self) -> Self {
Self {
effect: self.effect.clone(),
value: self.value.clone(),
}
}
}
impl<T: fmt::Debug> fmt::Debug for Derived<T> {
impl<T: fmt::Debug, F: ?Sized> fmt::Debug for Derived<T, F> {
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<T, F1: ?Sized, F2: ?Sized> CoerceUnsized<Derived<T, F2>> for Derived<T, F1> where F1: Unsize<F2> {}
/// Effect function
struct EffectFn<T, F: ?Sized> {
/// Value
// TODO: Remove the indirection of the inner signal here.
value: Signal<Option<T>>,
/// Function
f: F,
}
impl<T, F> FnOnce<()> for EffectFn<T, F>
where
F: Fn() -> T,
{
type Output = ();
extern "rust-call" fn call_once(mut self, args: ()) -> Self::Output {
self.call_mut(args)
}
}
impl<T, F> FnMut<()> for EffectFn<T, F>
where
F: Fn() -> T,
{
extern "rust-call" fn call_mut(&mut self, args: ()) -> Self::Output {
self.call(args)
}
}
impl<T, F> Fn<()> for EffectFn<T, F>
where
F: Fn() -> T,
{
extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
self.value.set(Some((self.f)()));
}
}

View File

@@ -1,7 +1,7 @@
//! Reactivity for `dynatos`
// Features
#![feature(unsize, coerce_unsized)]
#![feature(unsize, coerce_unsized, unboxed_closures, fn_traits)]
// Modules
pub mod derived;

View File

@@ -142,12 +142,7 @@ where
}
// TODO: Allow impl for `impl SignalGet<Value: WithDynText>`
#[duplicate::duplicate_item(
Sig;
[Signal];
[Derived];
)]
impl<T> WithDynAttr for Sig<T>
impl<T> WithDynAttr for Signal<T>
where
T: WithDynAttr,
{
@@ -159,6 +154,19 @@ where
}
}
impl<T, F> WithDynAttr for Derived<T, F>
where
T: WithDynAttr,
F: ?Sized,
{
fn with_attr<F2, O>(&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<Value: WithDynText>`
#[duplicate::duplicate_item(
Sig;
[Signal];
[Derived];
)]
impl<T> DynAttrPred for Sig<T>
impl<T> DynAttrPred for Signal<T>
where
T: DynAttrPred,
{
fn eval(&self) -> bool {
self.with(T::eval)
}
}
impl<T, F: ?Sized> DynAttrPred for Derived<T, F>
where
T: DynAttrPred,
{

View File

@@ -113,12 +113,7 @@ where
}
// TODO: Allow impl for `impl SignalGet<Value: WithDynText>`
#[duplicate::duplicate_item(
Sig;
[Signal];
[Derived];
)]
impl<T> WithDynText for Sig<T>
impl<T> WithDynText for Signal<T>
where
T: WithDynText,
{
@@ -129,6 +124,18 @@ where
self.with(|text| text.with_text(f))
}
}
impl<T, F> WithDynText for Derived<T, F>
where
T: WithDynText,
F: ?Sized,
{
fn with_text<F2, O>(&self, f: F2) -> O
where
F2: FnOnce(Option<&str>) -> O,
{
self.with(|text| text.with_text(f))
}
}
impl<T> WithDynText for QuerySignal<T>
where
T: WithDynText,

View File

@@ -140,12 +140,7 @@ impl ToDynProp for Ty {
}
// TODO: Allow impl for `impl SignalGet<Value: WithDynText>`
#[duplicate::duplicate_item(
Sig;
[Signal];
[Derived];
)]
impl<T> ToDynProp for Sig<T>
impl<T> ToDynProp for Signal<T>
where
T: ToDynProp,
{
@@ -153,6 +148,15 @@ where
self.with(|prop| prop.to_prop())
}
}
impl<T, F> ToDynProp for Derived<T, F>
where
T: ToDynProp,
F: ?Sized,
{
fn to_prop(&self) -> Option<JsValue> {
self.with(|prop| prop.to_prop())
}
}
impl<T> ToDynProp for QuerySignal<T>
where
T: ToDynProp,