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]>);
impl Deps {
pub fn as_slice(&self) -> &[NodeId] {
&self.0
}
#[allow(dead_code)]
pub(crate) fn into_inner(self) -> SmallVec<[NodeId; 8]> {
self.0
}
pub(crate) fn empty() -> Self {
Deps(SmallVec::new())
}
#[allow(dead_code)]
#[doc(hidden)]
pub fn from_signals(ids: &[NodeId]) -> Self {
let mut sv = SmallVec::new();
sv.extend_from_slice(ids);
Deps(sv)
}
}
pub trait IntoDeps {
fn into_deps(self) -> Deps;
}
impl IntoDeps for () {
fn into_deps(self) -> Deps {
Deps::empty()
}
}
impl IntoDeps for Deps {
fn into_deps(self) -> Deps {
self
}
}
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()
}
}
macro_rules! impl_into_deps_for_tuple {
($($name:ident),+) => {
impl<$($name: Trackable),+> IntoDeps for ($($name,)+) {
#[allow(non_snake_case)]
fn into_deps(self) -> Deps {
let ($($name,)+) = self;
let mut sv: SmallVec<[NodeId; 8]> = SmallVec::new();
$( sv.push($name.node_id()); )+
Deps(sv)
}
}
};
}
impl_into_deps_for_tuple!(T1);
impl_into_deps_for_tuple!(T1, T2);
impl_into_deps_for_tuple!(T1, T2, T3);
impl_into_deps_for_tuple!(T1, T2, T3, T4);
impl_into_deps_for_tuple!(T1, T2, T3, T4, T5);
impl_into_deps_for_tuple!(T1, T2, T3, T4, T5, T6);
impl_into_deps_for_tuple!(T1, T2, T3, T4, T5, T6, T7);
impl_into_deps_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8);
impl_into_deps_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_into_deps_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_into_deps_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_into_deps_for_tuple!(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
#[cfg(test)]
mod tests {
use rstest::rstest;
use serial_test::serial;
use super::*;
use crate::reactive::memo::Memo;
use crate::reactive::signal::Signal;
#[rstest]
#[serial(reactive_runtime)]
fn into_deps_unit_is_empty() {
let deps_input: () = ();
let deps = deps_input.into_deps();
assert!(deps.as_slice().is_empty());
}
#[rstest]
#[serial(reactive_runtime)]
fn into_deps_single_signal() {
let s = Signal::new(42_i32);
let deps = (s.clone(),).into_deps();
assert_eq!(deps.as_slice(), &[s.id()]);
}
#[rstest]
#[serial(reactive_runtime)]
fn into_deps_three_signals_preserves_order() {
let a = Signal::new(1_i32);
let b = Signal::new("two");
let c = Signal::new(3.0_f64);
let deps = (a.clone(), b.clone(), c.clone()).into_deps();
assert_eq!(deps.as_slice(), &[a.id(), b.id(), c.id()]);
}
#[rstest]
#[serial(reactive_runtime)]
fn into_deps_arity_12_compiles_and_collects() {
let s = [
Signal::new(0_i32),
Signal::new(1_i32),
Signal::new(2_i32),
Signal::new(3_i32),
Signal::new(4_i32),
Signal::new(5_i32),
Signal::new(6_i32),
Signal::new(7_i32),
Signal::new(8_i32),
Signal::new(9_i32),
Signal::new(10_i32),
Signal::new(11_i32),
];
let deps = (
s[0].clone(),
s[1].clone(),
s[2].clone(),
s[3].clone(),
s[4].clone(),
s[5].clone(),
s[6].clone(),
s[7].clone(),
s[8].clone(),
s[9].clone(),
s[10].clone(),
s[11].clone(),
)
.into_deps();
assert_eq!(deps.as_slice().len(), 12);
}
#[rstest]
#[serial(reactive_runtime)]
fn into_deps_with_memo_collects_memo_node_id() {
let signal = Signal::new(2_i32);
let signal_clone = signal.clone();
let memo = Memo::new(move || signal_clone.get() * 10);
let memo_id = memo.id();
let deps = (memo,).into_deps();
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()");
}
}