use rustc_hash::FxHashMap;
use std::any::{Any, TypeId};
use std::rc::Rc;
type Key = (Rc<str>, TypeId);
#[derive(Default)]
pub struct ElementStateMap {
current: FxHashMap<Key, Rc<dyn Any>>,
previous: FxHashMap<Key, Rc<dyn Any>>,
}
impl ElementStateMap {
pub fn begin_frame(&mut self) {
self.previous = std::mem::take(&mut self.current);
}
pub fn get_or_default<S: Default + 'static>(&mut self, id: &str) -> Rc<S> {
let key: Key = (Rc::from(id), TypeId::of::<S>());
let state = match self.current.get(&key) {
Some(state) => state.clone(),
None => {
let state = self.previous.remove(&key).unwrap_or_else(|| Rc::new(S::default()) as Rc<dyn Any>);
self.current.insert(key, state.clone());
state
}
};
state.downcast::<S>().unwrap_or_else(|_| Rc::new(S::default()))
}
pub fn len(&self) -> usize {
self.current.len()
}
pub fn is_empty(&self) -> bool {
self.current.is_empty()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::cell::Cell;
#[test]
fn state_survives_while_used_and_is_dropped_otherwise() {
let mut map = ElementStateMap::default();
map.get_or_default::<Cell<u32>>("lista").set(7);
map.begin_frame();
assert_eq!(map.get_or_default::<Cell<u32>>("lista").get(), 7, "pasa al frame siguiente");
assert!(!map.get_or_default::<Cell<bool>>("lista").get(), "otro tipo, otro estado");
map.begin_frame();
map.begin_frame();
assert_eq!(map.get_or_default::<Cell<u32>>("lista").get(), 0, "un frame sin uso lo libera");
}
}