use crate::UITree;
use std::any::Any;
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;
thread_local! {
static CACHE: RefCell<HashMap<u64, Rc<dyn Any>>> = RefCell::new(HashMap::new());
}
pub fn static_node<Msg: Clone + 'static>(
id: u64,
build: impl FnOnce() -> UITree<Msg>,
) -> UITree<Msg> {
CACHE.with(|cache| {
let mut cache = cache.borrow_mut();
if let Some(existing) = cache.get(&id) {
let rc = existing.clone();
if let Ok(tree) = rc.downcast::<UITree<Msg>>() {
return (*tree).clone();
}
panic!("appfront static_tree: duplicate static node id {id}");
}
let tree = build();
cache.insert(id, Rc::new(tree.clone()));
tree
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{NodeKind, UITree};
#[test]
fn build_runs_exactly_once_per_id() {
let calls = std::rc::Rc::new(std::cell::Cell::new(0usize));
let calls2 = std::rc::Rc::clone(&calls);
let built = static_node(1, || {
calls2.set(calls2.get() + 1);
UITree::<()>::container(|c| {
c.text("hello");
})
});
assert_eq!(calls.get(), 1);
assert!(matches!(built.kind, NodeKind::Container { .. }));
for _ in 0..5 {
let _again = static_node(1, || {
panic!("build must not run again for a cached id");
#[allow(unreachable_code)]
UITree::<()>::container(|c| {
c.text("never");
})
});
}
assert_eq!(calls.get(), 1);
}
#[test]
fn distinct_ids_cache_independently() {
let a = static_node(100, || {
UITree::<()>::container(|c| {
c.text("a");
})
});
let b = static_node(200, || {
UITree::<()>::container(|c| {
c.text("b");
})
});
match (&a.kind, &b.kind) {
(NodeKind::Container { children: ca }, NodeKind::Container { children: cb }) => {
match (&ca[0].kind, &cb[0].kind) {
(NodeKind::Text { text: ta }, NodeKind::Text { text: tb }) => {
assert_eq!(ta, "a");
assert_eq!(tb, "b");
}
_ => panic!("expected text nodes"),
}
}
_ => panic!("expected container nodes"),
}
}
#[test]
fn cached_tree_is_independent_clone() {
let first = static_node(300, || {
UITree::<()>::container(|c| {
c.text("x");
})
});
let second = static_node(300, || {
panic!("build must not run again for a cached id");
#[allow(unreachable_code)]
UITree::<()>::container(|c| {
c.text("should-not-build");
})
});
assert_eq!(format!("{first:?}"), format!("{second:?}"));
}
}