use crate::{Memo, ReadSignal, RwSignal, memo};
pub trait Source {
type Value;
fn read(&self) -> Self::Value;
}
impl<T: Clone + 'static> Source for RwSignal<T> {
type Value = T;
fn read(&self) -> T {
self.get()
}
}
impl<T: Clone + 'static> Source for ReadSignal<T> {
type Value = T;
fn read(&self) -> T {
self.get()
}
}
impl<T: Clone + 'static> Source for Memo<T> {
type Value = T;
fn read(&self) -> T {
self.get()
}
}
impl Source for f32 {
type Value = f32;
fn read(&self) -> f32 {
*self
}
}
impl Source for bool {
type Value = bool;
fn read(&self) -> bool {
*self
}
}
pub fn derive<S, U>(source: S, map: impl Fn(S::Value) -> U + 'static) -> Memo<U>
where
S: Source + 'static,
U: PartialEq + 'static,
{
memo(move || map(source.read()))
}
pub fn derive_pair<A, B, U>(
first: A,
second: B,
map: impl Fn(A::Value, B::Value) -> U + 'static,
) -> Memo<U>
where
A: Source + 'static,
B: Source + 'static,
U: PartialEq + 'static,
{
memo(move || map(first.read(), second.read()))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{reset_runtime, signal};
#[test]
fn a_derived_value_follows_its_source() {
reset_runtime();
let source = signal(2i32);
let doubled = derive(source.clone(), |n| n * 2);
assert_eq!(doubled.get(), 4, "seeded from the source, not a default");
source.set(5);
assert_eq!(doubled.get(), 10);
}
#[test]
fn a_pair_recomputes_when_either_half_moves() {
reset_runtime();
let level = signal(10i32);
let charging = signal(false);
let label = derive_pair(
level.read_only(),
charging.read_only(),
|level, charging| format!("{level}{}", if charging { "+" } else { "" }),
);
assert_eq!(label.get(), "10");
charging.set(true);
assert_eq!(label.get(), "10+");
level.set(11);
assert_eq!(label.get(), "11+");
}
#[test]
fn a_derivation_outlives_the_call_that_made_it() {
reset_runtime();
let source = signal(1i32);
let derived = derive(source.clone(), |n| n * 10);
let read: Box<dyn Fn() -> i32> = Box::new(move || derived.get());
source.set(7);
assert_eq!(read(), 70, "whatever holds the derivation keeps it alive");
}
#[test]
fn a_plain_value_reads_as_itself() {
assert_eq!(Source::read(&0.5f32), 0.5);
assert!(Source::read(&true));
}
#[test]
fn a_derivation_is_itself_a_source() {
reset_runtime();
let source = signal(3i32);
let once = derive(source.clone(), |n| n + 1);
let twice = derive(once, |n| n * 2);
assert_eq!(twice.get(), 8);
source.set(4);
assert_eq!(twice.get(), 10);
}
}