use crate::runtime::{Executor, Node};
use std::cell::RefCell;
use std::collections::HashMap;
use std::collections::hash_map::Entry;
use std::hash::Hash;
use std::rc::Rc;
#[derive(Clone)]
pub struct KeyedFactory<'a, K: Hash + Eq, T: 'static>(Rc<RefCell<KeyedFactoryInner<'a, K, T>>>);
impl<'a, K: Hash + Eq, T: 'static> Default for KeyedFactory<'a, K, T> {
fn default() -> Self {
Self(Rc::new(RefCell::new(KeyedFactoryInner::default())))
}
}
impl<'a, K: Hash + Eq, T: 'static> KeyedFactory<'a, K, T> {
pub fn attach(self, f: impl FnMut(&mut Executor, &K) -> Node<T> + 'a) -> Self {
{
let mut inner = self.0.borrow_mut();
inner.attach(f);
}
self
}
pub fn get(&self, executor: &mut Executor, key: K) -> Node<T> {
self.0.borrow_mut().get(executor, key)
}
}
struct KeyedFactoryInner<'a, K: Hash + Eq, T: 'static> {
cache: HashMap<K, Node<T>>,
factory: Option<Box<dyn FnMut(&mut Executor, &K) -> Node<T> + 'a>>,
}
impl<'a, K: Hash + Eq, T: 'static> Default for KeyedFactoryInner<'a, K, T> {
fn default() -> Self {
Self {
cache: HashMap::new(),
factory: None,
}
}
}
impl<'a, K: Hash + Eq, T: 'static> KeyedFactoryInner<'a, K, T> {
fn attach(&mut self, f: impl FnMut(&mut Executor, &K) -> Node<T> + 'a) {
assert!(self.factory.is_none(), "factory already attached");
self.factory = Some(Box::new(f));
}
fn get(&mut self, executor: &mut Executor, key: K) -> Node<T> {
let entry = self.cache.entry(key);
match entry {
Entry::Occupied(node) => node.get().to_owned(),
Entry::Vacant(entry) => {
let factory = self.factory.as_mut().expect("factory not attached");
let node = factory(executor, entry.key());
entry.insert(node).to_owned()
}
}
}
}