1use std::any::Any;
2
3pub(crate) struct ManagedState {
5    state: Vec<Tracked>,
6}
7
8enum Tracked {
9    Begin { id: u64, state: Box<dyn Any + Send + Sync> },
10    End,
11}
12
13#[doc(hidden)]
14pub struct ManagedStateTracker<'a> {
15    tracker: &'a mut ManagedState,
16    index: usize,
17}
18
19impl ManagedState {
20    pub fn tracker(&mut self) -> ManagedStateTracker {
24        ManagedStateTracker {
25            tracker: self,
26            index: 0,
27        }
28    }
29}
30
31impl Default for ManagedState {
32    fn default() -> Self {
33        Self { state: Vec::new() }
34    }
35}
36
37impl Tracked {
38    unsafe fn unchecked_mut_ref<'a, T: Any + Send + Sync>(&mut self) -> &'a mut T {
39        match self {
40            Tracked::Begin { state, .. } => {
41                let state = state
42                    .downcast_mut::<T>()
43                    .expect("widgets with the same id must always be of the same type");
44
45                (state as *mut T).as_mut().unwrap()
46            }
47            _ => unreachable!(),
48        }
49    }
50}
51
52impl<'a> ManagedStateTracker<'a> {
53    pub(crate) fn begin<'i, T, F>(&mut self, id: u64, default: F) -> &'i mut T
56    where
57        T: Any + Send + Sync,
58        F: FnOnce() -> T,
59    {
60        let search_start = self.index;
61        let mut level = 0;
62
63        while self.index < self.tracker.state.len() {
64            match &self.tracker.state[self.index] {
65                Tracked::End if level > 0 => level -= 1,
66                Tracked::End => {
67                    self.index = search_start;
69                    break;
70                }
71                &Tracked::Begin { id: tid, state: _ } if level == 0 && tid == id => {
72                    self.tracker.state.splice(search_start..self.index, None);
73                    unsafe {
74                        let i = search_start;
75                        self.index = search_start + 1;
76                        return self.tracker.state[i].unchecked_mut_ref();
77                    }
78                }
79                &Tracked::Begin { .. } => level += 1,
80            }
81            self.index += 1;
82        }
83
84        let i = self.index;
85        let state = Box::new(default()) as Box<dyn Any + Send + Sync>;
86        self.tracker.state.insert(i, Tracked::Begin { id, state });
87        self.tracker.state.insert(i + 1, Tracked::End);
88        self.index += 1;
89        unsafe { self.tracker.state[i].unchecked_mut_ref() }
90    }
91
92    pub(crate) fn end(&mut self) {
95        let search_start = self.index;
96        let mut level = 0;
97
98        while self.index < self.tracker.state.len() {
99            match &self.tracker.state[self.index] {
100                Tracked::Begin { .. } => {
101                    self.index += 1;
102                    level += 1;
103                }
104                Tracked::End if level > 0 => {
105                    self.index += 1;
106                    level -= 1;
107                }
108                Tracked::End => {
109                    self.tracker.state.splice(search_start..self.index, None);
111                    self.index = search_start + 1;
112                    return;
113                }
114            }
115        }
116
117        unreachable!("did not find `End` at the end.");
118    }
119}
120
121impl<'a> Drop for ManagedStateTracker<'a> {
122    fn drop(&mut self) {
123        while self.index < self.tracker.state.len() {
124            self.tracker.state.pop();
125        }
126    }
127}