lento 0.1.1

Cross platform ui framework
use crate::base::Id;
use crate::{some_or_continue, some_or_return};
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::rc::Rc;

pub struct ComputedValueInner<T> {
    next_id: u32,
    id2keys: HashMap<u32, Vec<String>>,
    consumers: HashMap<u32, Box<dyn FnMut(Vec<T>)>>,
    keys: HashMap<String, Vec<u32>>,
    values: HashMap<String, T>,
}

impl<T: Clone> ComputedValueInner<T> {
    fn new() -> Self {
        Self {
            next_id: 1,
            consumers: HashMap::new(),
            keys: HashMap::new(),
            values: HashMap::new(),
            id2keys: HashMap::new(),
        }
    }

    fn read_value(&self, key: &str) -> Option<T> {
        self.values.get(key).cloned()
    }

    fn get_values(&self, keys: &Vec<String>) -> Option<Vec<T>> {
        let mut result = Vec::new();
        for k in keys {
            let v = some_or_return!(self.values.get(k), None);
            result.push(v.clone());
        }
        Some(result)
    }

    pub fn register_dep<F: FnMut(Vec<T>)>(&mut self, keys: &Vec<String>, mut consumer: Box<dyn FnMut(Vec<T>)>) -> u32 {
        if let Some(v) = self.get_values(keys) {
            consumer(v);
        }
        let id = self.next_id;
        self.next_id += 1;
        self.id2keys.insert(id, keys.to_owned());
        self.consumers.insert(id, consumer);
        for k in keys {
            self.keys
                .entry(k.to_owned())
                .or_insert(Vec::new())
                .push(id);
        }
        id
    }
    pub fn update(&mut self, key: &str, value: T) {
        self.values.insert(key.to_owned(), value);
        if let Some(ids) = self.keys.get(key) {
            for id in ids {
                let keys = some_or_continue!(self.id2keys.get(id));
                let values = some_or_continue!(self.get_values(keys));
                let consumer = some_or_continue!(self.consumers.get_mut(id));
                consumer(values);
            }
        }
    }
    pub fn unregister_dep(&mut self, id: u32) {
        self.consumers.remove(&id);
        let keys = some_or_return!(self.id2keys.get(&id));
        for k in keys {
            let list = some_or_continue!(self.keys.get_mut(k));
            list.retain(|&x| x != id);
        }
    }
}

#[derive(Clone)]
pub struct ComputedValue<T> {
    inner: Rc<RefCell<ComputedValueInner<T>>>,
}

impl<T: Clone + 'static> ComputedValue<T> {
    pub fn new() -> Self {
        Self {
            inner: Rc::new(RefCell::new(ComputedValueInner::new())),
        }
    }
    pub fn update_value(&self, key: &str, value: T) {
        self.inner.borrow_mut().update(key, value);
    }
    pub fn dep<F: FnMut(Vec<T>) + 'static>(&self, keys: &Vec<String>, consumer: F) -> ComputedValueHandle {
        let mut inner = self.inner.borrow_mut();
        let id = inner.register_dep::<F>(keys, Box::new(consumer));
        let inner_clone = self.inner.clone();
        let dropper = Box::new(move || {
            let mut inner = inner_clone.borrow_mut();
            inner.unregister_dep(id);
        });
        ComputedValueHandle { dropper }
    }
}

pub struct ComputedValueHandle {
    dropper: Box<dyn FnMut()>,
}

impl Drop for ComputedValueHandle {
    fn drop(&mut self) {
        (self.dropper)()
    }
}

#[test]
fn test_computed_value() {
    let mut cv = ComputedValue::new();
    let counter = Rc::new(Cell::new(0));
    {
        let counter2 = counter.clone();
        let h1 = cv.dep(&vec!["counter".to_string()], move|v| {
            counter2.set(v[0]);
        });
        cv.update_value("counter", 1);
        assert_eq!(counter.get(), 1);
    }
    cv.update_value("counter", 2);
    assert_eq!(counter.get(), 1);
}