use alloc::rc::Rc;
use core::{cell::RefCell, panic::Location};
use crate::{Signal, SignalIdentity, map::Map, watcher::Context};
#[derive(Debug, Clone)]
pub struct Zip<A, B> {
a: A,
b: B,
discriminator: usize,
}
struct ZipWatchState<L, R, W> {
latest_left: RefCell<L>,
latest_right: RefCell<R>,
watcher: W,
}
impl<A, B> Zip<A, B>
where
A: Signal,
B: Signal,
A::Output: Clone,
B::Output: Clone,
{
#[track_caller]
pub fn new(a: A, b: B) -> Self {
Self {
a,
b,
discriminator: SignalIdentity::call_site_discriminator::<(A, B)>(Location::caller()),
}
}
}
pub trait FlattenMap<F, T, Output>: Signal {
#[track_caller]
fn flatten_map(&self, f: F) -> Map<Self, impl Clone + Fn(Self::Output) -> Output, Output>;
}
impl<C, F, T1, T2, Output> FlattenMap<F, (T1, T2), Output> for C
where
C: Signal<Output = (T1, T2)> + 'static,
F: 'static + Clone + Fn(T1, T2) -> Output,
T1: 'static,
T2: 'static,
Output: 'static,
{
#[track_caller]
fn flatten_map(&self, f: F) -> Map<C, impl Clone + Fn((T1, T2)) -> Output, Output> {
Map::new(self.clone(), move |(t1, t2)| f(t1, t2))
}
}
impl<C, F, T1, T2, T3, Output> FlattenMap<F, (T1, T2, T3), Output> for C
where
C: Signal<Output = ((T1, T2), T3)> + 'static,
F: 'static + Clone + Fn(T1, T2, T3) -> Output,
Output: 'static,
{
#[track_caller]
fn flatten_map(&self, f: F) -> Map<C, impl Clone + Fn(((T1, T2), T3)) -> Output, Output> {
Map::new(self.clone(), move |((t1, t2), t3)| f(t1, t2, t3))
}
}
#[track_caller]
pub fn zip<A, B>(a: A, b: B) -> Zip<A, B>
where
A: Signal,
B: Signal,
A::Output: Clone,
B::Output: Clone,
{
Zip::new(a, b)
}
impl<A, B> Signal for Zip<A, B>
where
A: Signal,
B: Signal,
A::Output: Clone,
B::Output: Clone,
{
type Output = (A::Output, B::Output);
type Guard = (A::Guard, B::Guard);
fn get(&self) -> Self::Output {
let Self { a, b, .. } = self;
(a.get(), b.get())
}
fn identity(&self) -> Option<SignalIdentity> {
Some(
self.a
.identity()?
.combine(self.b.identity()?)
.with_discriminator(self.discriminator),
)
}
fn watch(&self, watcher: impl Fn(Context<Self::Output>) + 'static) -> Self::Guard {
let Self { a, b, .. } = self;
let state = Rc::new(ZipWatchState {
latest_left: RefCell::new(a.get()),
latest_right: RefCell::new(b.get()),
watcher,
});
let guard_a = {
let state = Rc::clone(&state);
self.a.watch(move |ctx: Context<A::Output>| {
let updated_a = ctx.value().clone();
*state.latest_left.borrow_mut() = updated_a;
let other = state.latest_right.borrow().clone();
(state.watcher)(ctx.map(|value| (value, other)));
})
};
let guard_b = self.b.watch(move |ctx: Context<B::Output>| {
let updated_b = ctx.value().clone();
*state.latest_right.borrow_mut() = updated_b;
let other = state.latest_left.borrow().clone();
(state.watcher)(ctx.map(|value| (other, value)));
});
(guard_a, guard_b)
}
}