use smallvec::SmallVec;
use super::memo::Memo;
use super::runtime::NodeId;
use super::signal::Signal;
pub trait Trackable {
fn node_id(&self) -> NodeId;
}
#[allow(dead_code)]
#[derive(Debug)]
pub struct Deps(SmallVec<[NodeId; 8]>);
#[derive(Debug)]
pub struct ExplicitDeps(Deps);
impl ExplicitDeps {
#[doc(hidden)]
pub fn from_node_ids(ids: impl IntoIterator<Item = NodeId>) -> Self {
let mut nodes = SmallVec::new();
nodes.extend(ids);
Self(Deps(nodes))
}
#[doc(hidden)]
pub fn into_deps(self) -> Deps {
self.0
}
#[doc(hidden)]
pub fn as_slice(&self) -> &[NodeId] {
self.0.as_slice()
}
}
#[derive(Debug)]
pub enum ReactiveDeps {
Explicit(ExplicitDeps),
Auto,
}
impl From<ExplicitDeps> for ReactiveDeps {
fn from(deps: ExplicitDeps) -> Self {
Self::Explicit(deps)
}
}
impl Deps {
pub fn as_slice(&self) -> &[NodeId] {
&self.0
}
#[allow(dead_code)]
pub(crate) fn into_inner(self) -> SmallVec<[NodeId; 8]> {
self.0
}
#[allow(dead_code)]
#[doc(hidden)]
pub fn from_signals(ids: &[NodeId]) -> Self {
let mut sv = SmallVec::new();
sv.extend_from_slice(ids);
Deps(sv)
}
}
impl<T: 'static> Trackable for Signal<T> {
fn node_id(&self) -> NodeId {
self.id()
}
}
impl<T: Clone + 'static> Trackable for Memo<T> {
fn node_id(&self) -> NodeId {
self.id()
}
}
impl<T: Trackable + ?Sized> Trackable for &T {
fn node_id(&self) -> NodeId {
(*self).node_id()
}
}
#[macro_export]
macro_rules! deps {
($($dependency:expr),* $(,)?) => {{
$crate::reactive::ExplicitDeps::from_node_ids([
$($crate::reactive::Trackable::node_id(&$dependency),)*
])
}};
}
#[macro_export]
macro_rules! deps_auto {
() => {
$crate::reactive::ReactiveDeps::Auto
};
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use serial_test::serial;
use crate::reactive::memo::Memo;
use crate::reactive::signal::Signal;
#[rstest]
#[serial(reactive_runtime)]
fn explicit_deps_macro_collects_signal_node_ids() {
crate::reactive::ReactiveScope::run(|| {
let signal = Signal::new(42_i32);
let deps = crate::deps![signal];
assert_eq!(deps.as_slice(), &[signal.id()]);
});
}
#[rstest]
#[serial(reactive_runtime)]
fn explicit_deps_with_memo_collects_memo_node_id() {
crate::reactive::ReactiveScope::run(|| {
let signal = Signal::new(2_i32);
let memo = Memo::new(move || signal.get() * 10);
let memo_id = memo.id();
let deps = crate::deps![memo];
let slice = deps.as_slice();
assert_eq!(
slice.len(),
1,
"single-element tuple of Memo must yield one NodeId"
);
assert_eq!(slice[0], memo_id, "deps element must be Memo::id()");
});
}
}