1use std::cell::RefCell;
2use std::collections::VecDeque;
3use std::rc::Rc;
4
5thread_local! {
6 pub static RUNTIME: RefCell<Runtime> = RefCell::new(Runtime::new());
7}
8
9type EffectId = usize;
10type EffectFn = Rc<dyn Fn()>;
11
12pub struct Runtime {
13 effects: Vec<Option<EffectFn>>,
14 current_effect: Option<EffectId>,
15 pending_effects: VecDeque<EffectId>,
16 is_batching: bool,
17}
18
19impl Runtime {
20 pub fn new() -> Self {
21 Self {
22 effects: Vec::new(),
23 current_effect: None,
24 pending_effects: VecDeque::new(),
25 is_batching: false,
26 }
27 }
28
29 pub fn current_effect(&self) -> Option<EffectId> {
30 self.current_effect
31 }
32
33 pub fn register_effect(&mut self, f: impl Fn() + 'static) -> EffectId {
34 let id = self.effects.len();
35 self.effects.push(Some(Rc::new(f)));
36 id
37 }
38
39 pub fn set_current_effect(&mut self, id: Option<EffectId>) -> Option<EffectId> {
40 let prev = self.current_effect;
41 self.current_effect = id;
42 prev
43 }
44
45 pub fn get_effect(&self, id: EffectId) -> Option<&EffectFn> {
46 self.effects.get(id).and_then(|e| e.as_ref())
47 }
48
49 pub fn clone_effect(&self, id: EffectId) -> Option<EffectFn> {
50 self.effects.get(id).and_then(|e| e.as_ref().map(Rc::clone))
51 }
52
53 pub fn schedule_effect(&mut self, id: EffectId) {
54 if !self.pending_effects.contains(&id) {
55 self.pending_effects.push_back(id);
56 }
57 }
58
59 pub fn is_batching(&self) -> bool {
60 self.is_batching
61 }
62
63 pub fn pop_pending_effect(&mut self) -> Option<EffectId> {
64 self.pending_effects.pop_front()
65 }
66
67 pub fn start_batch(&mut self) -> bool {
68 let was_batching = self.is_batching;
69 self.is_batching = true;
70 was_batching
71 }
72
73 pub fn end_batch(&mut self, was_batching: bool) {
74 self.is_batching = was_batching;
75 }
76}
77
78impl Default for Runtime {
79 fn default() -> Self {
80 Self::new()
81 }
82}