guinea_core/scope/
state.rs1use std::any::{Any, TypeId};
2use std::cell::{Ref, RefCell};
3use std::collections::HashMap;
4use std::rc::Rc;
5use std::sync::atomic::{AtomicU64, Ordering};
6
7use crate::actor::shape::Declared;
8
9use super::{Scope, ScopeData};
10
11static NEXT_SUBSCRIBER_ID: AtomicU64 = AtomicU64::new(0);
12
13pub struct Subscription {
14 unsubscribe: Option<Box<dyn FnOnce()>>,
15}
16
17impl Subscription {
18 pub(crate) fn inert() -> Self {
21 Self { unsubscribe: None }
22 }
23}
24
25impl Drop for Subscription {
26 fn drop(&mut self) {
27 if let Some(unsubscribe) = self.unsubscribe.take() {
28 unsubscribe();
29 }
30 }
31}
32
33pub struct StateHandle<T>(Rc<RefCell<T>>);
34
35impl<T> StateHandle<T> {
36 pub fn borrow(&self) -> Ref<'_, T> {
37 self.0.borrow()
38 }
39}
40
41impl<T> Clone for StateHandle<T> {
42 fn clone(&self) -> Self {
43 Self(self.0.clone())
44 }
45}
46
47impl<T> From<Rc<RefCell<T>>> for StateHandle<T> {
48 fn from(inner: Rc<RefCell<T>>) -> Self {
49 Self(inner)
50 }
51}
52
53pub trait Reducer: Clone + Default + std::fmt::Debug + 'static {
86 type Update: Clone + 'static;
90
91 fn reduce(&mut self, update: Self::Update);
92}
93
94pub type Slot<R> = RefCell<Rc<R>>;
97
98pub(super) struct Cell {
99 state: Rc<dyn Any>,
100 listeners: RefCell<Vec<(u64, Rc<dyn Fn()>)>>,
101 observers: RefCell<Vec<(u64, Rc<dyn Fn(&dyn Any)>)>>,
102}
103
104#[derive(Clone, Copy)]
106pub(super) struct Kind {
107 name: &'static str,
108 describe: fn(&dyn Any) -> String,
109}
110
111impl Kind {
112 fn of<R: Reducer>() -> Self {
113 Self {
114 name: std::any::type_name::<R>(),
115 describe: |state| match state.downcast_ref::<Slot<R>>() {
116 Some(cell) => match cell.try_borrow() {
117 Ok(state) => format!("{:#?}", **state),
118 Err(_) => "<being changed>".to_string(),
119 },
120 None => "<unknown>".to_string(),
121 },
122 }
123 }
124}
125
126#[derive(Clone, Debug, PartialEq)]
128pub struct DescribedState {
129 pub type_name: &'static str,
130 pub state: String,
131 pub feature: Option<&'static str>,
133 pub declared: Option<Declared>,
135}
136
137impl Cell {
138 fn empty<R: Reducer>() -> Self {
139 Self::holding(Rc::new(Slot::new(Rc::new(R::default()))))
140 }
141
142 fn holding(state: Rc<dyn Any>) -> Self {
143 Cell {
144 state,
145 listeners: RefCell::new(Vec::new()),
146 observers: RefCell::new(Vec::new()),
147 }
148 }
149}
150
151impl Scope {
152 pub fn describe_states(&self) -> Vec<DescribedState> {
154 let Some(data) = self.data() else {
155 return Vec::new();
156 };
157 let cells = data.cells.borrow();
158 let kinds = data.kinds.borrow();
159 let owners = data.owners.borrow();
160 let mut described: Vec<DescribedState> = cells
161 .iter()
162 .filter_map(|(type_id, cell)| {
163 let kind = kinds.get(type_id)?;
164 Some(DescribedState {
165 type_name: kind.name,
166 state: (kind.describe)(&*cell.state),
167 feature: owners
168 .get(type_id)
169 .and_then(|section| data.section_name(*section)),
170 declared: data.declarations.borrow().get(type_id).copied(),
171 })
172 })
173 .collect();
174 described.sort_by_key(|state| state.type_name);
175 described
176 }
177
178 pub fn state<R: Reducer>(&self) -> Rc<Slot<R>> {
183 match self.data() {
184 Some(data) => data.state::<R>(),
185 None => Rc::new(Slot::new(Rc::new(R::default()))),
186 }
187 }
188
189 pub fn peek<R: Reducer>(&self) -> Option<Rc<Slot<R>>> {
190 let data = self.data()?;
191 data.note_kind::<R>();
192 let cells = data.cells.borrow();
193 let cell = cells.get(&TypeId::of::<R>())?;
194 Some(
195 cell.state
196 .clone()
197 .downcast::<Slot<R>>()
198 .expect("Scope cell type mismatch for this TypeId - unreachable, keyed by R"),
199 )
200 }
201
202 pub fn seed<R: Reducer>(&self, state: R) {
206 let data = self.installing("seeding a reducer");
207 data.note_kind::<R>();
208 let replaced = data
209 .cells
210 .borrow_mut()
211 .insert(TypeId::of::<R>(), Cell::holding(Rc::new(Slot::new(Rc::new(state)))));
212 drop(replaced);
213 }
214
215 pub fn push<R: Reducer>(&self, update: R::Update) {
223 let Some(data) = self.data() else { return };
224 let carried: Option<Box<dyn Any>> = data
225 .is_observed(TypeId::of::<R>())
226 .then(|| Box::new(update.clone()) as Box<dyn Any>);
227 let state = data.state::<R>();
228 drop(data);
229
230 {
231 let mut state = state.borrow_mut();
232 Rc::make_mut(&mut state).reduce(update);
233 }
234
235 crate::notify::mark(*self, TypeId::of::<R>(), carried);
236 }
237
238 pub fn observe<R: Reducer>(&self, callback: impl Fn(&R::Update) + 'static) -> Subscription {
246 let Some(data) = self.data() else {
247 return Subscription::inert();
248 };
249 let id = NEXT_SUBSCRIBER_ID.fetch_add(1, Ordering::Relaxed);
250 {
251 let mut cells = data.cells.borrow_mut();
252 let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
253 cell.observers.borrow_mut().push((
254 id,
255 Rc::new(move |update: &dyn Any| {
256 if let Some(update) = update.downcast_ref::<R::Update>() {
257 callback(update);
258 }
259 }),
260 ));
261 }
262
263 let scope = *self;
264 Subscription {
265 unsubscribe: Some(Box::new(move || {
266 let Some(data) = scope.data() else { return };
267 if let Some(cell) = data.cells.borrow_mut().get_mut(&TypeId::of::<R>()) {
268 cell.observers.borrow_mut().retain(|(oid, _)| *oid != id);
269 }
270 })),
271 }
272 }
273
274 pub(crate) fn listeners_of(&self, cell: TypeId) -> Vec<Rc<dyn Fn()>> {
275 let Some(data) = self.data() else {
276 return Vec::new();
277 };
278 let cells = data.cells.borrow();
279 cells
280 .get(&cell)
281 .map(|cell| cell.listeners.borrow().iter().map(|(_, f)| f.clone()).collect())
282 .unwrap_or_default()
283 }
284
285 pub(crate) fn observers_of(&self, cell: TypeId) -> Vec<Rc<dyn Fn(&dyn Any)>> {
286 let Some(data) = self.data() else {
287 return Vec::new();
288 };
289 let cells = data.cells.borrow();
290 cells
291 .get(&cell)
292 .map(|cell| cell.observers.borrow().iter().map(|(_, f)| f.clone()).collect())
293 .unwrap_or_default()
294 }
295
296 pub fn subscribe<R: Reducer>(&self, callback: impl Fn() + 'static) -> Subscription {
297 let Some(data) = self.data() else {
298 return Subscription::inert();
299 };
300 let id = NEXT_SUBSCRIBER_ID.fetch_add(1, Ordering::Relaxed);
301 {
302 let mut cells = data.cells.borrow_mut();
303 let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
304 cell.listeners.borrow_mut().push((id, Rc::new(callback)));
305 }
306
307 let scope = *self;
308 Subscription {
309 unsubscribe: Some(Box::new(move || {
310 let Some(data) = scope.data() else { return };
311 if let Some(cell) = data.cells.borrow_mut().get_mut(&TypeId::of::<R>()) {
312 cell.listeners.borrow_mut().retain(|(lid, _)| *lid != id);
313 }
314 })),
315 }
316 }
317
318 pub fn binding<R: Reducer>(&self) -> crate::binding::ReducerBinding<R> {
321 crate::binding::ReducerBinding::new(*self)
322 }
323
324 pub fn snapshot_states(&self) -> HashMap<TypeId, Rc<dyn Any>> {
328 let Some(data) = self.data() else {
329 return HashMap::new();
330 };
331 data.cells
332 .borrow()
333 .iter()
334 .map(|(type_id, cell)| (*type_id, cell.state.clone()))
335 .collect()
336 }
337
338 pub fn restore_states(&self, states: HashMap<TypeId, Rc<dyn Any>>) {
343 let data = self.installing("restoring state");
344 let mut replaced = Vec::new();
345 {
346 let mut cells = data.cells.borrow_mut();
347 for (type_id, state) in states {
348 replaced.extend(cells.insert(type_id, Cell::holding(state)));
349 }
350 }
351 drop(replaced);
352 }
353}
354
355impl ScopeData {
356 fn note_kind<R: Reducer>(&self) {
357 self.kinds
358 .borrow_mut()
359 .entry(TypeId::of::<R>())
360 .or_insert_with(Kind::of::<R>);
361 }
362
363 fn state<R: Reducer>(&self) -> Rc<Slot<R>> {
364 self.note_kind::<R>();
365 let mut cells = self.cells.borrow_mut();
366 let cell = cells.entry(TypeId::of::<R>()).or_insert_with(Cell::empty::<R>);
367 cell.state
368 .clone()
369 .downcast::<Slot<R>>()
370 .expect("Scope cell type mismatch for this TypeId - unreachable, keyed by R")
371 }
372
373 fn is_observed(&self, cell: TypeId) -> bool {
374 self.cells
375 .borrow()
376 .get(&cell)
377 .is_some_and(|cell| !cell.observers.borrow().is_empty())
378 }
379}