lemon 0.2.0-alpha.0

A reactive UI toolkit for Rust
Documentation
use std::cell::RefCell;
use std::rc::Weak;

pub trait Subscriber {
    fn mark_dirty(&self);
}

thread_local! {
    static OBSERVER_STACK: RefCell<Vec<Weak<dyn Subscriber>>> = RefCell::new(Vec::new());
}

struct StackGuard;

impl Drop for StackGuard {
    fn drop(&mut self) {
        OBSERVER_STACK.with(|stack| {
            stack.borrow_mut().pop();
        });
    }
}

pub fn with_observer<R>(observer: Weak<dyn Subscriber>, f: impl FnOnce() -> R) -> R {
    OBSERVER_STACK.with(|stack| stack.borrow_mut().push(observer));
    let _guard = StackGuard;
    f()
}

pub fn current_observer() -> Option<Weak<dyn Subscriber>> {
    OBSERVER_STACK.with(|stack| stack.borrow().last().cloned())
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::Cell;
    use std::rc::Rc;

    struct MockSub(Cell<u32>);
    impl Subscriber for MockSub {
        fn mark_dirty(&self) {
            self.0.set(self.0.get() + 1);
        }
    }

    #[test]
    fn no_observer_outside_scope() {
        assert!(current_observer().is_none());
    }

    #[test]
    fn observer_present_inside_with_observer() {
        let sub = Rc::new(MockSub(Cell::new(0)));
        with_observer(Rc::downgrade(&sub) as Weak<dyn Subscriber>, || {
            assert!(current_observer().is_some());
        });
        assert!(current_observer().is_none());
    }

    #[test]
    fn with_observer_returns_closure_value() {
        let sub = Rc::new(MockSub(Cell::new(0)));
        let result = with_observer(Rc::downgrade(&sub) as Weak<dyn Subscriber>, || 42);
        assert_eq!(result, 42);
    }
}