telar-reactive-core 0.1.5

Fine-grained reactive graph for Telar: signals, memos, effects and batching.
Documentation
use std::marker::PhantomData;

use crate::runtime::{self, SignalId};

fn read_with<T: 'static, R>(id: SignalId, f: impl FnOnce(&T) -> R) -> R {
    runtime::track_signal(id);
    runtime::with_signal_value::<T, R>(id, f)
}

fn peek_with<T: 'static, R>(id: SignalId, f: impl FnOnce(&T) -> R) -> R {
    runtime::with_signal_value::<T, R>(id, f)
}

pub struct ReadSignal<T: 'static> {
    pub(crate) id: SignalId,
    _marker: PhantomData<T>,
}

impl<T: 'static> Clone for ReadSignal<T> {
    fn clone(&self) -> Self {
        runtime::clone_signal(self.id);
        ReadSignal {
            id: self.id,
            _marker: PhantomData,
        }
    }
}

impl<T: 'static> Drop for ReadSignal<T> {
    fn drop(&mut self) {
        runtime::drop_signal(self.id);
    }
}

impl<T: Clone + 'static> ReadSignal<T> {
    pub fn get(&self) -> T {
        read_with::<T, T>(self.id, |v| v.clone())
    }

    pub fn peek(&self) -> T {
        peek_with::<T, T>(self.id, |v| v.clone())
    }
}

impl<T: 'static> ReadSignal<T> {
    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
        read_with::<T, R>(self.id, f)
    }
}

pub struct RwSignal<T: 'static> {
    pub(crate) id: SignalId,
    _marker: PhantomData<T>,
}

impl<T: 'static> Clone for RwSignal<T> {
    fn clone(&self) -> Self {
        runtime::clone_signal(self.id);
        RwSignal {
            id: self.id,
            _marker: PhantomData,
        }
    }
}

impl<T: 'static> Drop for RwSignal<T> {
    fn drop(&mut self) {
        runtime::drop_signal(self.id);
    }
}

impl<T: 'static> RwSignal<T> {
    pub fn set(&self, value: T) {
        runtime::set_signal_value(self.id, value);
        runtime::notify_signal(self.id);
    }

    pub fn update(&self, f: impl FnOnce(&mut T)) {
        runtime::update_signal_value::<T>(self.id, f);
        runtime::notify_signal(self.id);
    }

    pub fn with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
        read_with::<T, R>(self.id, f)
    }

    /// As [`with`](Self::with), but does not subscribe the caller — for reads from an event handler, where
    /// tracking would attach the value to whatever effect happens to be running.
    pub fn peek_with<R>(&self, f: impl FnOnce(&T) -> R) -> R {
        peek_with::<T, R>(self.id, f)
    }

    pub fn read_only(&self) -> ReadSignal<T> {
        runtime::clone_signal(self.id);
        ReadSignal {
            id: self.id,
            _marker: PhantomData,
        }
    }
}

impl<T: Clone + 'static> RwSignal<T> {
    pub fn get(&self) -> T {
        read_with::<T, T>(self.id, |v| v.clone())
    }

    pub fn peek(&self) -> T {
        peek_with::<T, T>(self.id, |v| v.clone())
    }
}

impl RwSignal<bool> {
    /// Flip a boolean signal in place — sugar for `.update(|v| *v = !*v)`, so `$flag.toggle()` reads cleanly.
    pub fn toggle(&self) {
        self.update(|v| *v = !*v);
    }
}

pub fn signal<T: 'static>(value: T) -> RwSignal<T> {
    let id = runtime::create_signal_storage(value, 1);
    RwSignal {
        id,
        _marker: PhantomData,
    }
}