freya_core/lifecycle/
state.rs

1use 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
70/// Allow calling the states as functions.
71/// Limited to `Copy` values only.
72impl<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    /// Adapted from https://github.com/DioxusLabs/dioxus/blob/a4aef33369894cd6872283d6d7d265303ae63913/packages/signals/src/read.rs#L246
82    /// SAFETY: You must call this function directly with `self` as the argument.
83    /// This function relies on the size of the object you return from the deref
84    /// being the same as the object you pass in
85    #[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        // https://github.com/dtolnay/case-studies/tree/master/callable-types
92
93        // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
94        let uninit_callable = MaybeUninit::<Self>::uninit();
95        // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
96        let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
97
98        // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
99        let size_of_closure = std::mem::size_of_val(&uninit_closure);
100        assert_eq!(size_of_closure, std::mem::size_of::<Self>());
101
102        // Then cast the lifetime of the closure to the lifetime of &self.
103        fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
104            b
105        }
106        let reference_to_closure = cast_lifetime(
107            {
108                // The real closure that we will never use.
109                &uninit_closure
110            },
111            #[allow(clippy::missing_transmute_annotations)]
112            // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self.
113            unsafe {
114                std::mem::transmute(state)
115            },
116        );
117
118        // Cast the closure to a trait object.
119        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/// Given a type `T` you may pass an owned value, a `State<T>` or a function returning a `ReadRef<T>`
139#[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    /// Create a [State] attached to the current Scope.
260    pub fn create(value: T) -> Self
261    where
262        T: 'static, // TODO: Move this lifetime bound to impl
263    {
264        Self::create_in_scope(value, None)
265    }
266
267    /// Create a [State] attached to the given Scope.
268    pub fn create_in_scope(value: T, scope_id: impl Into<Option<ScopeId>>) -> Self
269    where
270        T: 'static,
271    {
272        // TODO: Move this lifetime bound to impl
273        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    /// Create a global [State] that is expected to live until the end of the process.
285    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}