1use dioxus_core::ScopeId;
2use generational_box::BorrowResult;
3use std::{any::Any, cell::RefCell, collections::HashMap, ops::Deref, panic::Location, rc::Rc};
4
5mod memo;
6pub use memo::*;
7
8mod signal;
9pub use signal::*;
10
11use crate::{Readable, ReadableRef, Signal, Writable, WritableRef};
12
13pub trait InitializeFromFunction<T> {
15 fn initialize_from_function(f: fn() -> T) -> Self;
17}
18
19impl<T> InitializeFromFunction<T> for T {
20 fn initialize_from_function(f: fn() -> T) -> Self {
21 f()
22 }
23}
24
25pub struct Global<T, R = T> {
27 constructor: fn() -> R,
28 key: GlobalKey<'static>,
29 phantom: std::marker::PhantomData<fn() -> T>,
30}
31
32impl<T: Clone + 'static, R: Clone + 'static> Deref for Global<T, R>
36where
37 T: Readable<Target = R> + InitializeFromFunction<R>,
38{
39 type Target = dyn Fn() -> R;
40
41 fn deref(&self) -> &Self::Target {
42 unsafe { Readable::deref_impl(self) }
43 }
44}
45
46impl<T: Clone + 'static, R: 'static> Readable for Global<T, R>
47where
48 T: Readable<Target = R> + InitializeFromFunction<R>,
49{
50 type Target = R;
51 type Storage = T::Storage;
52
53 #[track_caller]
54 fn try_read_unchecked(
55 &self,
56 ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
57 self.resolve().try_read_unchecked()
58 }
59
60 #[track_caller]
61 fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
62 self.resolve().try_peek_unchecked()
63 }
64}
65
66impl<T: Clone + 'static, R: 'static> Writable for Global<T, R>
67where
68 T: Writable<Target = R> + InitializeFromFunction<R>,
69{
70 type Mut<'a, Read: ?Sized + 'static> = T::Mut<'a, Read>;
71
72 fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
73 ref_: Self::Mut<'_, I>,
74 f: F,
75 ) -> Self::Mut<'_, U> {
76 T::map_mut(ref_, f)
77 }
78
79 fn try_map_mut<
80 I: ?Sized + 'static,
81 U: ?Sized + 'static,
82 F: FnOnce(&mut I) -> Option<&mut U>,
83 >(
84 ref_: Self::Mut<'_, I>,
85 f: F,
86 ) -> Option<Self::Mut<'_, U>> {
87 T::try_map_mut(ref_, f)
88 }
89
90 fn downcast_lifetime_mut<'a: 'b, 'b, Read: ?Sized + 'static>(
91 mut_: Self::Mut<'a, Read>,
92 ) -> Self::Mut<'b, Read> {
93 T::downcast_lifetime_mut(mut_)
94 }
95
96 #[track_caller]
97 fn try_write_unchecked(
98 &self,
99 ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
100 self.resolve().try_write_unchecked()
101 }
102}
103
104impl<T: Clone + 'static, R: 'static> Global<T, R>
105where
106 T: Writable<Target = R> + InitializeFromFunction<R>,
107{
108 pub fn write(&self) -> T::Mut<'static, R> {
110 self.resolve().try_write_unchecked().unwrap()
111 }
112
113 #[track_caller]
116 pub fn with_mut<O>(&self, f: impl FnOnce(&mut R) -> O) -> O {
117 self.resolve().with_mut(f)
118 }
119}
120
121impl<T: Clone + 'static, R> Global<T, R>
122where
123 T: InitializeFromFunction<R>,
124{
125 #[track_caller]
126 pub const fn new(constructor: fn() -> R) -> Self {
128 let key = std::panic::Location::caller();
129 Self {
130 constructor,
131 key: GlobalKey::new(key),
132 phantom: std::marker::PhantomData,
133 }
134 }
135
136 #[track_caller]
140 pub const fn with_name(constructor: fn() -> R, key: &'static str) -> Self {
141 Self {
142 constructor,
143 key: GlobalKey::File {
144 file: key,
145 line: 0,
146 column: 0,
147 index: 0,
148 },
149 phantom: std::marker::PhantomData,
150 }
151 }
152
153 #[track_caller]
157 pub const fn with_location(
158 constructor: fn() -> R,
159 file: &'static str,
160 line: u32,
161 column: u32,
162 index: usize,
163 ) -> Self {
164 Self {
165 constructor,
166 key: GlobalKey::File {
167 file,
168 line: line as _,
169 column: column as _,
170 index: index as _,
171 },
172 phantom: std::marker::PhantomData,
173 }
174 }
175
176 pub fn key(&self) -> GlobalKey<'static> {
178 self.key.clone()
179 }
180
181 pub fn resolve(&self) -> T {
184 let key = self.key();
185
186 let context = get_global_context();
187
188 {
190 let read = context.map.borrow();
191 if let Some(signal) = read.get(&key) {
192 return signal.downcast_ref::<T>().cloned().unwrap();
193 }
194 }
195 let signal = ScopeId::ROOT.in_runtime(|| T::initialize_from_function(self.constructor));
198 context
199 .map
200 .borrow_mut()
201 .insert(key, Box::new(signal.clone()));
202 signal
203 }
204
205 pub fn origin_scope(&self) -> ScopeId {
207 ScopeId::ROOT
208 }
209}
210
211#[derive(Clone, Default)]
213pub struct GlobalLazyContext {
214 map: Rc<RefCell<HashMap<GlobalKey<'static>, Box<dyn Any>>>>,
215}
216
217#[derive(Clone, Debug, PartialEq, Eq, Hash)]
219pub enum GlobalKey<'a> {
220 File {
222 file: &'a str,
224
225 line: u32,
227
228 column: u32,
230
231 index: u32,
233 },
234
235 Raw(&'a str),
237}
238
239impl<'a> GlobalKey<'a> {
240 pub const fn new(key: &'a Location<'a>) -> Self {
242 GlobalKey::File {
243 file: key.file(),
244 line: key.line(),
245 column: key.column(),
246 index: 0,
247 }
248 }
249}
250
251impl From<&'static Location<'static>> for GlobalKey<'static> {
252 fn from(key: &'static Location<'static>) -> Self {
253 Self::new(key)
254 }
255}
256
257impl GlobalLazyContext {
258 pub fn get_signal_with_key<T>(&self, key: GlobalKey) -> Option<Signal<T>> {
261 self.map.borrow().get(&key).map(|f| {
262 *f.downcast_ref::<Signal<T>>().unwrap_or_else(|| {
263 panic!(
264 "Global signal with key {:?} is not of the expected type. Keys are {:?}",
265 key,
266 self.map.borrow().keys()
267 )
268 })
269 })
270 }
271
272 #[doc(hidden)]
273 pub fn clear<T: 'static>(&self) {
275 self.map.borrow_mut().retain(|_k, v| !v.is::<T>());
276 }
277}
278
279pub fn get_global_context() -> GlobalLazyContext {
281 match ScopeId::ROOT.has_context() {
282 Some(context) => context,
283 None => ScopeId::ROOT.provide_context(Default::default()),
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
294 fn test_global_keys() {
295 const MYSIGNAL: GlobalSignal<i32> = GlobalSignal::new(|| 42);
297 const MYSIGNAL2: GlobalSignal<i32> = GlobalSignal::new(|| 42);
298 const MYSIGNAL3: GlobalSignal<i32> = GlobalSignal::with_name(|| 42, "custom-keyed");
299
300 let a = MYSIGNAL.key();
301 let b = MYSIGNAL.key();
302 let c = MYSIGNAL.key();
303 assert_eq!(a, b);
304 assert_eq!(b, c);
305
306 let d = MYSIGNAL2.key();
307 assert_ne!(a, d);
308
309 let e = MYSIGNAL3.key();
310 assert_ne!(a, e);
311 }
312}