freya_core/lifecycle/
state.rs1use std::{
2 cell::RefCell,
3 mem::MaybeUninit,
4 ops::Deref,
5 rc::Rc,
6};
7
8use generational_box::{
9 AnyStorage,
10 GenerationalBox,
11 UnsyncStorage,
12};
13use rustc_hash::FxHashSet;
14
15use crate::{
16 current_context::CurrentContext,
17 prelude::use_hook,
18 reactive_context::ReactiveContext,
19 scope_id::ScopeId,
20};
21
22pub trait MutView<'a, T: 'static> {
23 fn read(&mut self) -> ReadRef<'a, T>;
24
25 fn peek(&mut self) -> ReadRef<'a, T>;
26
27 fn write(&mut self) -> WriteRef<'a, T>;
28
29 fn write_if(&mut self, with: impl FnOnce(WriteRef<'a, T>) -> bool);
30}
31
32impl<T: 'static> MutView<'static, T> for State<T> {
33 fn read(&mut self) -> ReadRef<'static, T> {
34 if let Some(mut rc) = ReactiveContext::try_current() {
35 rc.subscribe(&self.subscribers.read());
36 }
37 self.key.read()
38 }
39
40 fn peek(&mut self) -> ReadRef<'static, T> {
41 self.key.read()
42 }
43
44 fn write(&mut self) -> WriteRef<'static, T> {
45 self.subscribers.write().borrow_mut().retain(|s| s.notify());
46 self.key.write()
47 }
48
49 fn write_if(&mut self, with: impl FnOnce(WriteRef<'static, T>) -> bool) {
50 let chnaged = with(self.key.write());
51 if chnaged {
52 self.subscribers.write().borrow_mut().retain(|s| s.notify());
53 }
54 }
55}
56
57pub struct State<T> {
58 key: GenerationalBox<T>,
59 subscribers: GenerationalBox<Rc<RefCell<FxHashSet<ReactiveContext>>>>,
60}
61
62impl<T: 'static> PartialEq for State<T> {
63 fn eq(&self, other: &Self) -> bool {
64 self.key.ptr_eq(&other.key)
65 }
66}
67
68impl<T: 'static> Eq for State<T> {}
69
70impl<T: Copy + 'static> Deref for State<T> {
73 type Target = dyn Fn() -> T;
74
75 fn deref(&self) -> &Self::Target {
76 unsafe { State::deref_impl(self) }
77 }
78}
79
80impl<T> State<T> {
81 #[doc(hidden)]
86 unsafe fn deref_impl<'a>(state: &State<T>) -> &'a dyn Fn() -> T
87 where
88 Self: Sized + 'a,
89 T: Clone + 'static,
90 {
91 let uninit_callable = MaybeUninit::<Self>::uninit();
95 let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
97
98 let size_of_closure = std::mem::size_of_val(&uninit_closure);
100 assert_eq!(size_of_closure, std::mem::size_of::<Self>());
101
102 fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
104 b
105 }
106 let reference_to_closure = cast_lifetime(
107 {
108 &uninit_closure
110 },
111 #[allow(clippy::missing_transmute_annotations)]
112 unsafe {
114 std::mem::transmute(state)
115 },
116 );
117
118 reference_to_closure as &_
120 }
121}
122
123impl<T: std::ops::Not<Output = T> + Clone + 'static> State<T> {
124 pub fn toggled(&mut self) -> T {
125 let value = self.read().clone();
126 let neg_value = !value;
127 self.set(neg_value.clone());
128 neg_value
129 }
130
131 pub fn toggle(&mut self) {
132 self.toggled();
133 }
134}
135
136type ReadStateFunc<T> = Rc<dyn Fn() -> ReadRef<'static, T>>;
137
138#[derive(Clone)]
140pub enum ReadState<T: 'static> {
141 State(State<T>),
142 Func(ReadStateFunc<T>),
143 Owned(T),
144}
145
146impl<T> From<T> for ReadState<T> {
147 fn from(value: T) -> Self {
148 ReadState::Owned(value)
149 }
150}
151
152impl<T> From<State<T>> for ReadState<T> {
153 fn from(value: State<T>) -> Self {
154 ReadState::State(value)
155 }
156}
157
158impl<T: PartialEq> PartialEq for ReadState<T> {
159 fn eq(&self, other: &ReadState<T>) -> bool {
160 match (self, other) {
161 (Self::State(a), Self::State(b)) => a == b,
162 (Self::Func(a), Self::Func(b)) => Rc::ptr_eq(a, b),
163 (Self::Owned(a), Self::Owned(b)) => a == b,
164 _ => false,
165 }
166 }
167}
168impl<T: 'static> ReadState<T> {
169 pub fn read(&'_ self) -> ReadStateCow<'_, T> {
170 match self {
171 Self::Func(f) => ReadStateCow::Ref(f()),
172 Self::State(s) => ReadStateCow::Ref(s.read()),
173 Self::Owned(o) => ReadStateCow::Borrowed(o),
174 }
175 }
176}
177
178pub enum ReadStateCow<'a, T: 'static> {
179 Ref(ReadRef<'static, T>),
180 Borrowed(&'a T),
181}
182
183impl<'a, T> Deref for ReadStateCow<'a, T> {
184 type Target = T;
185 fn deref(&self) -> &Self::Target {
186 match self {
187 Self::Ref(r) => r.deref(),
188 Self::Borrowed(b) => b,
189 }
190 }
191}
192
193pub type ReadRef<'a, T> =
194 <generational_box::UnsyncStorage as generational_box::AnyStorage>::Ref<'a, T>;
195
196pub type WriteRef<'a, T> =
197 <generational_box::UnsyncStorage as generational_box::AnyStorage>::Mut<'a, T>;
198
199impl<T> State<T> {
200 pub fn read(&self) -> ReadRef<'static, T> {
201 if let Some(mut rc) = ReactiveContext::try_current() {
202 rc.subscribe(&self.subscribers.read());
203 }
204 self.key.read()
205 }
206
207 pub fn peek(&self) -> ReadRef<'static, T> {
208 self.key.read()
209 }
210
211 pub fn write(&mut self) -> WriteRef<'static, T> {
212 self.subscribers.write().borrow_mut().retain(|s| s.notify());
213 self.key.write()
214 }
215
216 pub fn with_mut(&mut self, with: impl FnOnce(WriteRef<'static, T>))
217 where
218 T: 'static,
219 {
220 self.subscribers.write().borrow_mut().retain(|s| s.notify());
221 with(self.key.write());
222 }
223
224 pub fn write_unchecked(&self) -> WriteRef<'static, T> {
225 for subscriber in self.subscribers.write().borrow_mut().iter() {
226 subscriber.notify();
227 }
228 self.key.write()
229 }
230
231 pub fn set(&mut self, value: T)
232 where
233 T: 'static,
234 {
235 *self.write() = value;
236 }
237
238 pub fn set_if_modified(&mut self, value: T)
239 where
240 T: 'static + PartialEq,
241 {
242 let is_equal = *self.peek() == value;
243 if !is_equal {
244 self.set(value);
245 }
246 }
247
248 pub fn set_if_modified_and_then(&mut self, value: T, then: impl FnOnce())
249 where
250 T: 'static + PartialEq,
251 {
252 let is_equal = *self.peek() == value;
253 if !is_equal {
254 self.set(value);
255 then();
256 }
257 }
258
259 pub fn create(value: T) -> Self
261 where
262 T: 'static, {
264 Self::create_in_scope(value, None)
265 }
266
267 pub fn create_in_scope(value: T, scope_id: impl Into<Option<ScopeId>>) -> Self
269 where
270 T: 'static,
271 {
272 let owner = CurrentContext::with(|context| {
274 let scopes_storages = context.scopes_storages.borrow_mut();
275
276 let scopes_storage = scopes_storages.get(&scope_id.into().unwrap_or(context.scope_id));
277 scopes_storage.unwrap().owner.clone()
278 });
279 let key = owner.insert(value);
280 let subscribers = owner.insert(Rc::default());
281 State { key, subscribers }
282 }
283
284 pub fn create_global(value: T) -> Self
286 where
287 T: 'static,
288 {
289 let owner = UnsyncStorage::owner();
290 Box::leak(Box::new(owner.clone()));
291 let key = owner.insert(value);
292 let subscribers = owner.insert(Rc::default());
293 State { key, subscribers }
294 }
295}
296
297impl<T> Clone for State<T> {
298 fn clone(&self) -> Self {
299 *self
300 }
301}
302
303impl<T> Copy for State<T> {}
304
305impl<T> State<Option<T>> {
306 pub fn take(&mut self) -> Option<T>
307 where
308 T: 'static,
309 {
310 self.write().take()
311 }
312}
313
314pub fn use_state<T: 'static>(init: impl FnOnce() -> T) -> State<T> {
315 use_hook(|| State::create(init()))
316}