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