use std::fmt::Debug;
use super::XSignal;
use super::depth::Depth;
use crate::debug_correlation_id::DebugCorrelationId;
use crate::prelude::OrElseLog as _;
use crate::prelude::diagnostics::debug;
use crate::prelude::diagnostics::debug_span;
use crate::prelude::diagnostics::trace;
use crate::prelude::diagnostics::warn;
use crate::string::XString;
impl<T> XSignal<T> {
pub fn derive<U>(
&self,
name: impl Into<XString>,
to: impl Fn(&T) -> U + 'static,
from: impl Fn(&T, &U) -> Option<T> + 'static,
) -> XSignal<U>
where
T: Debug + 'static,
U: Debug + Eq + 'static,
{
let main = self;
let main_weak = main.downgrade();
let main_name =
DebugCorrelationId::new(|| format!("Main:{}", main.0.producer.name()).into());
let derived = XSignal::new(
name,
to(main.0.current_value.lock().or_throw("main").value()),
);
let derived_weak = derived.downgrade();
let derived_name =
DebugCorrelationId::new(|| format!("Derived:{}", derived.0.producer.name()).into());
let span = debug_span! { "Derived signal", main = %main_name, derived = %derived_name };
let _span = span.clone().entered();
debug!(main = %main_name, derived = %derived_name, "Make");
{
let main_weak = main_weak.clone();
let derived_weak = derived_weak.clone();
let span = span.clone();
trace!("Derived updates Main");
let derived_producer = &derived.0.producer;
let consumer =
derived_producer.register(derived_name, Depth::zero(), move |_version| {
let _span = span.enter();
debug!("Derived updated");
let Some(derived) = derived_weak.upgrade() else {
warn!("Derived is dropped but triggered");
debug_assert!(false);
return;
};
let Some(main) = main_weak.upgrade() else {
warn!("Main is dropped but subscribed");
debug_assert!(false);
return;
};
let t = from(
main.0.current_value.lock().or_throw("main").value(),
derived.0.current_value.lock().or_throw("derived").value(),
);
if let Some(t) = t {
main.force(t);
}
});
let mut on_main_drop = main.0.on_drop.lock().or_throw("on_drop");
on_main_drop.push(Box::new(move || drop(consumer)));
}
{
let main_weak = main_weak.clone();
let derived_weak = derived_weak.clone();
let span = span.clone();
trace!("Main updates Derived");
let main_producer = &main.0.producer;
let consumer = main_producer.register(main_name, Depth::zero(), move |_version| {
let _span = span.enter();
debug!("Main updated");
let Some(main) = main_weak.upgrade() else {
warn!("Main is dropped but triggered");
debug_assert!(false);
return;
};
let Some(derived) = derived_weak.upgrade() else {
warn!("Derived is dropped but subscribed");
debug_assert!(false);
return;
};
let t = to(main.0.current_value.lock().or_throw("main").value());
derived.set(t);
});
let mut on_drop = derived.0.on_drop.lock().or_throw("on_drop");
on_drop.push(Box::new(move || drop(consumer)));
}
return derived;
}
pub fn view<U>(&self, name: impl Into<XString>, to: impl Fn(&T) -> U + 'static) -> XSignal<U>
where
T: Debug + 'static,
U: Debug + Eq + 'static,
{
self.derive(name, to, |_, _| None)
}
}
pub fn if_change<T: Eq, U>(
from: impl Fn(&T, &U) -> Option<T> + 'static,
) -> impl Fn(&T, &U) -> Option<T> + 'static {
move |old_t, u| from(old_t, u).filter(|new_t| new_t != old_t)
}