1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
use std::{cell::RefCell, sync::Arc};

#[derive(Default)]
pub struct Engine {
    current_reaction: RefCell<Option<Reaction>>,
    pub(crate) current_update: RefCell<Option<Update>>,
}

impl Engine {
    #[must_use]
    pub fn new() -> Self {
        Self::default()
    }

    pub(crate) fn track(&self, subscriptions: &Arc<RefCell<SubscriptionList>>) {
        let mut reaction = self.current_reaction.borrow_mut();
        let reaction = match reaction.as_mut() {
            Some(reaction) => reaction,
            None => return,
        };
        subscriptions
            .borrow_mut()
            .push(reaction.subscriptions.clone());
    }

    pub fn batch(self: &Arc<Self>) -> Batch {
        let mut current_update = self.current_update.borrow_mut();
        let root = current_update.is_none();
        if !root {
            return Batch { engine: None };
        }

        *current_update = Some(Update::new());
        Batch {
            engine: Some(self.clone()),
        }
    }

    pub fn react(&self, mut f: impl FnMut() + 'static) {
        let mut current_reaction = self.current_reaction.borrow_mut();
        assert!(current_reaction.is_none());
        *current_reaction = Some(Reaction::new());
        drop(current_reaction);

        f();

        let mut current_reaction = self.current_reaction.borrow_mut();
        let reaction = current_reaction.take().unwrap();
        reaction
            .subscriptions
            .borrow_mut()
            .push(Arc::new(RefCell::new(f)));
    }
}

fn slow_pop_front<T>(xs: &mut Vec<T>) -> Option<T> {
    if xs.is_empty() {
        None
    } else {
        Some(xs.remove(0))
    }
}

type SubscriptionList = Vec<Arc<RefCell<Vec<Subscription>>>>;
type Subscription = Arc<RefCell<dyn FnMut()>>;

struct Reaction {
    subscriptions: Arc<RefCell<Vec<Subscription>>>,
}

impl Reaction {
    pub fn new() -> Self {
        Reaction {
            subscriptions: Arc::new(RefCell::new(Vec::new())),
        }
    }
}

pub(crate) struct Update {
    updates: Vec<Box<dyn FnOnce()>>,
}

impl Update {
    pub fn new() -> Self {
        Update {
            updates: Vec::new(),
        }
    }
}

#[must_use]
pub struct Batch {
    engine: Option<Arc<Engine>>,
}

impl Drop for Batch {
    fn drop(&mut self) {
        let engine = match &mut self.engine {
            Some(x) => x,
            None => return,
        };

        loop {
            let head = {
                let mut update = engine.current_update.borrow_mut();
                slow_pop_front(&mut update.as_mut().unwrap().updates)
            };
            let head = match head {
                Some(x) => x,
                None => break,
            };
            head();
        }

        engine.current_update.borrow_mut().take().unwrap();
    }
}

#[cfg(test)]
mod tests {
    use crate::instance::{Atom, Engine};
    use std::{cell::RefCell, sync::Arc};

    #[test]
    fn react_simple() {
        let engine = Arc::new(Engine::new());
        let atom = Atom::new(engine.clone(), 1);
        let sink = Arc::new(RefCell::new(Vec::new()));
        engine.react({
            let atom = atom.clone();
            let sink = sink.clone();
            move || {
                sink.borrow_mut().push(*atom.get());
            }
        });
        assert_eq!(*sink.borrow(), [1]);
        atom.set(2);
        assert_eq!(*sink.borrow(), [1, 2]);
    }
}