freya_core/lifecycle/
state.rs

1use std::{
2    cell::RefCell,
3    mem::MaybeUninit,
4    ops::Deref,
5    rc::Rc,
6};
7
8use generational_box::GenerationalBox;
9use rustc_hash::FxHashSet;
10
11use crate::{
12    current_context::CurrentContext,
13    prelude::use_hook,
14    reactive_context::ReactiveContext,
15    scope_id::ScopeId,
16};
17
18pub trait MutView<'a, T: 'static> {
19    fn read(&mut self) -> ReadRef<'a, T>;
20
21    fn peek(&mut self) -> ReadRef<'a, T>;
22
23    fn write(&mut self) -> WriteRef<'a, T>;
24
25    fn write_if(&mut self, with: impl FnOnce(WriteRef<'a, T>) -> bool);
26}
27
28impl<T: 'static> MutView<'static, T> for State<T> {
29    fn read(&mut self) -> ReadRef<'static, T> {
30        if let Some(mut rc) = ReactiveContext::try_current() {
31            rc.subscribe(&self.subscribers.read());
32        }
33        self.key.read()
34    }
35
36    fn peek(&mut self) -> ReadRef<'static, T> {
37        self.key.read()
38    }
39
40    fn write(&mut self) -> WriteRef<'static, T> {
41        self.subscribers.write().borrow_mut().retain(|s| s.notify());
42        self.key.write()
43    }
44
45    fn write_if(&mut self, with: impl FnOnce(WriteRef<'static, T>) -> bool) {
46        let chnaged = with(self.key.write());
47        if chnaged {
48            self.subscribers.write().borrow_mut().retain(|s| s.notify());
49        }
50    }
51}
52
53pub struct State<T> {
54    key: GenerationalBox<T>,
55    subscribers: GenerationalBox<Rc<RefCell<FxHashSet<ReactiveContext>>>>,
56}
57
58impl<T: 'static> PartialEq for State<T> {
59    fn eq(&self, other: &Self) -> bool {
60        self.key.ptr_eq(&other.key)
61    }
62}
63
64impl<T: 'static> Eq for State<T> {}
65
66/// Allow calling the states as functions.
67/// Limited to `Copy` values only.
68impl<T: Copy + 'static> Deref for State<T> {
69    type Target = dyn Fn() -> T;
70
71    fn deref(&self) -> &Self::Target {
72        unsafe { State::deref_impl(self) }
73    }
74}
75
76impl<T> State<T> {
77    /// Adapted from https://github.com/DioxusLabs/dioxus/blob/a4aef33369894cd6872283d6d7d265303ae63913/packages/signals/src/read.rs#L246
78    /// SAFETY: You must call this function directly with `self` as the argument.
79    /// This function relies on the size of the object you return from the deref
80    /// being the same as the object you pass in
81    #[doc(hidden)]
82    unsafe fn deref_impl<'a>(state: &State<T>) -> &'a dyn Fn() -> T
83    where
84        Self: Sized + 'a,
85        T: Clone + 'static,
86    {
87        // https://github.com/dtolnay/case-studies/tree/master/callable-types
88
89        // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
90        let uninit_callable = MaybeUninit::<Self>::uninit();
91        // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
92        let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
93
94        // 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.
95        let size_of_closure = std::mem::size_of_val(&uninit_closure);
96        assert_eq!(size_of_closure, std::mem::size_of::<Self>());
97
98        // Then cast the lifetime of the closure to the lifetime of &self.
99        fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
100            b
101        }
102        let reference_to_closure = cast_lifetime(
103            {
104                // The real closure that we will never use.
105                &uninit_closure
106            },
107            #[allow(clippy::missing_transmute_annotations)]
108            // 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.
109            unsafe {
110                std::mem::transmute(state)
111            },
112        );
113
114        // Cast the closure to a trait object.
115        reference_to_closure as &_
116    }
117}
118
119impl<T: std::ops::Not<Output = T> + Clone + 'static> State<T> {
120    pub fn toggled(&mut self) -> T {
121        let value = self.read().clone();
122        let neg_value = !value;
123        self.set(neg_value.clone());
124        neg_value
125    }
126
127    pub fn toggle(&mut self) {
128        self.toggled();
129    }
130}
131
132type ReadStateFunc<T> = Rc<dyn Fn() -> ReadRef<'static, T>>;
133
134/// Given a type `T` you may pass an owned value, a `State<T>` or a function returning a `ReadRef<T>`
135#[derive(Clone)]
136pub enum ReadState<T: 'static> {
137    State(State<T>),
138    Func(ReadStateFunc<T>),
139    Owned(T),
140}
141
142impl<T> From<T> for ReadState<T> {
143    fn from(value: T) -> Self {
144        ReadState::Owned(value)
145    }
146}
147
148impl<T> From<State<T>> for ReadState<T> {
149    fn from(value: State<T>) -> Self {
150        ReadState::State(value)
151    }
152}
153
154impl<T: PartialEq> PartialEq for ReadState<T> {
155    fn eq(&self, other: &ReadState<T>) -> bool {
156        match (self, other) {
157            (Self::State(a), Self::State(b)) => a == b,
158            (Self::Func(a), Self::Func(b)) => Rc::ptr_eq(a, b),
159            (Self::Owned(a), Self::Owned(b)) => a == b,
160            _ => false,
161        }
162    }
163}
164impl<T: 'static> ReadState<T> {
165    pub fn read(&'_ self) -> ReadStateCow<'_, T> {
166        match self {
167            Self::Func(f) => ReadStateCow::Ref(f()),
168            Self::State(s) => ReadStateCow::Ref(s.read()),
169            Self::Owned(o) => ReadStateCow::Borrowed(o),
170        }
171    }
172}
173
174pub enum ReadStateCow<'a, T: 'static> {
175    Ref(ReadRef<'static, T>),
176    Borrowed(&'a T),
177}
178
179impl<'a, T> Deref for ReadStateCow<'a, T> {
180    type Target = T;
181    fn deref(&self) -> &Self::Target {
182        match self {
183            Self::Ref(r) => r.deref(),
184            Self::Borrowed(b) => b,
185        }
186    }
187}
188
189pub type ReadRef<'a, T> =
190    <generational_box::UnsyncStorage as generational_box::AnyStorage>::Ref<'a, T>;
191
192pub type WriteRef<'a, T> =
193    <generational_box::UnsyncStorage as generational_box::AnyStorage>::Mut<'a, T>;
194
195impl<T> State<T> {
196    pub fn read(&self) -> ReadRef<'static, T> {
197        if let Some(mut rc) = ReactiveContext::try_current() {
198            rc.subscribe(&self.subscribers.read());
199        }
200        self.key.read()
201    }
202
203    pub fn peek(&self) -> ReadRef<'static, T> {
204        self.key.read()
205    }
206
207    pub fn write(&mut self) -> WriteRef<'static, T> {
208        self.subscribers.write().borrow_mut().retain(|s| s.notify());
209        self.key.write()
210    }
211
212    pub fn with_mut(&mut self, with: impl FnOnce(WriteRef<'static, T>))
213    where
214        T: 'static,
215    {
216        self.subscribers.write().borrow_mut().retain(|s| s.notify());
217        with(self.key.write());
218    }
219
220    pub fn write_unchecked(&self) -> WriteRef<'static, T> {
221        for subscriber in self.subscribers.write().borrow_mut().iter() {
222            subscriber.notify();
223        }
224        self.key.write()
225    }
226
227    pub fn set(&mut self, value: T)
228    where
229        T: 'static,
230    {
231        *self.write() = value;
232    }
233
234    pub fn set_if_modified(&mut self, value: T)
235    where
236        T: 'static + PartialEq,
237    {
238        let is_equal = *self.peek() == value;
239        if !is_equal {
240            self.set(value);
241        }
242    }
243
244    pub fn set_if_modified_and_then(&mut self, value: T, then: impl FnOnce())
245    where
246        T: 'static + PartialEq,
247    {
248        let is_equal = *self.peek() == value;
249        if !is_equal {
250            self.set(value);
251            then();
252        }
253    }
254
255    pub fn create(value: T) -> Self
256    where
257        T: 'static, // TODO: Move this lifetime bound to impl
258    {
259        Self::create_in_scope(value, None)
260    }
261
262    pub fn create_in_scope(value: T, scope_id: impl Into<Option<ScopeId>>) -> Self
263    where
264        T: 'static,
265    {
266        // TODO: Move this lifetime bound to impl
267        let owner = CurrentContext::with(|context| {
268            let scopes_storages = context.scopes_storages.borrow_mut();
269
270            let scopes_storage = scopes_storages.get(&scope_id.into().unwrap_or(context.scope_id));
271            scopes_storage.unwrap().owner.clone()
272        });
273        let key = owner.insert(value);
274        let subscribers = owner.insert(Rc::default());
275        State { key, subscribers }
276    }
277}
278
279impl<T> Clone for State<T> {
280    fn clone(&self) -> Self {
281        *self
282    }
283}
284
285impl<T> Copy for State<T> {}
286
287impl<T> State<Option<T>> {
288    pub fn take(&mut self) -> Option<T>
289    where
290        T: 'static,
291    {
292        self.write().take()
293    }
294}
295
296pub fn use_state<T: 'static>(init: impl FnOnce() -> T) -> State<T> {
297    use_hook(|| State::create(init()))
298}