use core::any::Any;
use alloc::{boxed::Box, rc::Rc};
use crate::{
SignalExt, SignalIdentity, constant,
watcher::{BoxWatcherGuard, Context, Watcher},
};
use super::Signal;
pub struct Computed<T>(pub(crate) Box<dyn ComputedImpl<Output = T>>);
#[allow(clippy::redundant_pub_crate)]
pub(crate) trait ComputedImpl: Any {
type Output;
fn compute(&self) -> Self::Output;
fn add_watcher(&self, watcher: Watcher<Self::Output>) -> BoxWatcherGuard;
fn identity(&self) -> Option<SignalIdentity>;
fn cloned(&self) -> Computed<Self::Output>;
}
impl<C: Signal + 'static> ComputedImpl for C {
type Output = C::Output;
fn compute(&self) -> Self::Output {
<Self as Signal>::get(self)
}
fn add_watcher(&self, watcher: Watcher<Self::Output>) -> BoxWatcherGuard {
Box::new(<Self as Signal>::watch(self, move |ctx| watcher(ctx)))
}
fn identity(&self) -> Option<SignalIdentity> {
<Self as Signal>::identity(self)
}
fn cloned(&self) -> Computed<Self::Output> {
self.clone().computed()
}
}
impl_signal_ops!(Computed<T>, [T], T);
impl<T: 'static + Clone + Default> Default for Computed<T> {
fn default() -> Self {
Self::constant(T::default())
}
}
impl<T> core::fmt::Debug for Computed<T> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str(core::any::type_name::<Self>())
}
}
impl<T: 'static> Signal for Computed<T> {
type Output = T;
type Guard = BoxWatcherGuard;
fn get(&self) -> Self::Output {
self.0.compute()
}
fn identity(&self) -> Option<SignalIdentity> {
self.0.identity()
}
fn watch(&self, watcher: impl Fn(Context<Self::Output>) + 'static) -> Self::Guard {
self.0.add_watcher(Rc::new(watcher))
}
}
impl<T: 'static> Clone for Computed<T> {
fn clone(&self) -> Self {
self.0.cloned()
}
}
impl<T> Computed<T> {
pub fn new<C>(value: C) -> Self
where
C: Signal<Output = T> + Clone + 'static,
{
Self(Box::new(value))
}
}
impl<T: 'static + Clone> Computed<T> {
pub fn constant(value: T) -> Self {
Self::new(constant(value))
}
}