1use crate::{
2 composer::{ComposePtr, Node, Runtime},
3 data::Data,
4 use_context, use_ref, Scope, ScopeData, ScopeState,
5};
6use alloc::borrow::Cow;
7use alloc::rc::Rc;
8use core::{
9 any::TypeId,
10 cell::{Cell, RefCell, UnsafeCell},
11 fmt, mem,
12};
13use slotmap::{DefaultKey, SlotMap};
14
15mod catch;
16pub use self::catch::{catch, Catch};
17
18mod dyn_compose;
19pub use self::dyn_compose::{dyn_compose, DynCompose};
20
21mod from_fn;
22pub use self::from_fn::{from_fn, FromFn};
23
24mod from_iter;
25pub use self::from_iter::{from_iter, FromIter};
26
27mod memo;
28pub use self::memo::{memo, Memo};
29
30#[must_use = "Composables do nothing unless composed or returned from other composables."]
49pub trait Compose: Data {
50 fn compose(cx: Scope<Self>) -> impl Compose;
52
53 #[doc(hidden)]
54 fn name() -> Option<Cow<'static, str>> {
55 let name = core::any::type_name::<Self>();
56 Some(
57 name.split('<')
58 .next()
59 .unwrap_or(name)
60 .split("::")
61 .last()
62 .unwrap_or(name)
63 .into(),
64 )
65 }
66}
67
68impl Compose for () {
69 fn compose(cx: Scope<Self>) -> impl Compose {
70 let _ = cx;
71 }
72}
73
74impl<C: Compose> Compose for Option<C> {
75 fn compose(cx: Scope<Self>) -> impl Compose {
76 let child_key = use_ref(&cx, || Cell::new(None));
77
78 let rt = Runtime::current();
79 let mut nodes = rt.nodes.borrow_mut();
80
81 if let Some(content) = &*cx.me() {
82 if let Some(key) = child_key.get() {
83 let last = nodes.get_mut(key).unwrap();
84
85 let ptr = content as *const dyn AnyCompose;
86 let ptr: *const dyn AnyCompose = unsafe { mem::transmute(ptr) };
87
88 *last.compose.borrow_mut() = ComposePtr::Ptr(ptr);
89
90 drop(nodes);
91
92 rt.queue(key);
93 } else {
94 let ptr: *const dyn AnyCompose =
95 unsafe { mem::transmute(content as *const dyn AnyCompose) };
96 let key = nodes.insert(Rc::new(Node {
97 compose: RefCell::new(crate::composer::ComposePtr::Ptr(ptr)),
98 scope: ScopeData::default(),
99 parent: Some(rt.current_key.get()),
100 children: RefCell::new(Vec::new()),
101 child_idx: 0,
102 }));
103 child_key.set(Some(key));
104
105 nodes
106 .get(rt.current_key.get())
107 .unwrap()
108 .children
109 .borrow_mut()
110 .push(key);
111
112 let child_state = &nodes[key].scope;
113
114 *child_state.contexts.borrow_mut() = cx.contexts.borrow().clone();
115 child_state
116 .contexts
117 .borrow_mut()
118 .values
119 .extend(cx.child_contexts.borrow().values.clone());
120
121 drop(nodes);
122
123 rt.queue(key);
124 }
125 } else if let Some(key) = child_key.get() {
126 child_key.set(None);
127
128 drop_node(&mut nodes, key);
129 }
130 }
131}
132
133fn drop_node(nodes: &mut SlotMap<DefaultKey, Rc<Node>>, key: DefaultKey) {
135 let node = nodes[key].clone();
136 if let Some(parent) = node.parent {
137 let parent = nodes.get_mut(parent).unwrap();
138 parent.children.borrow_mut().retain(|&x| x != key);
139 }
140
141 let children = node.children.borrow().clone();
142 for key in children {
143 drop_node(nodes, key)
144 }
145
146 nodes.remove(key);
147}
148
149#[derive(Data, thiserror::Error)]
153#[actuate(path = "crate")]
154pub struct Error {
155 make_error: Box<dyn Fn() -> Box<dyn core::error::Error>>,
156}
157
158impl Error {
159 pub fn new(error: impl core::error::Error + Clone + 'static) -> Self {
161 Self {
162 make_error: Box::new(move || Box::new(error.clone())),
163 }
164 }
165}
166
167impl fmt::Debug for Error {
168 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
169 (self.make_error)().fmt(f)
170 }
171}
172
173impl fmt::Display for Error {
174 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
175 (self.make_error)().fmt(f)
176 }
177}
178
179impl<C: Compose> Compose for Result<C, Error> {
180 fn compose(cx: Scope<Self>) -> impl Compose {
181 let catch_cx = use_context::<CatchContext>(&cx).unwrap();
182
183 let child_key = use_ref(&cx, || Cell::new(None));
184
185 let rt = Runtime::current();
186
187 match &*cx.me() {
188 Ok(content) => {
189 if let Some(key) = child_key.get() {
190 let mut nodes = rt.nodes.borrow_mut();
191 let last = nodes.get_mut(key).unwrap();
192
193 let ptr = content as *const dyn AnyCompose;
194 let ptr: *const dyn AnyCompose = unsafe { mem::transmute(ptr) };
195
196 *last.compose.borrow_mut() = ComposePtr::Ptr(ptr);
197
198 drop(nodes);
199
200 rt.queue(key);
201 } else {
202 let mut nodes = rt.nodes.borrow_mut();
203 let ptr: *const dyn AnyCompose =
204 unsafe { mem::transmute(content as *const dyn AnyCompose) };
205 let key = nodes.insert(Rc::new(Node {
206 compose: RefCell::new(crate::composer::ComposePtr::Ptr(ptr)),
207 scope: ScopeData::default(),
208 parent: Some(rt.current_key.get()),
209 children: RefCell::new(Vec::new()),
210 child_idx: 0,
211 }));
212 child_key.set(Some(key));
213
214 nodes
215 .get(rt.current_key.get())
216 .unwrap()
217 .children
218 .borrow_mut()
219 .push(key);
220
221 let child_state = &nodes[key].scope;
222
223 *child_state.contexts.borrow_mut() = cx.contexts.borrow().clone();
224 child_state
225 .contexts
226 .borrow_mut()
227 .values
228 .extend(cx.child_contexts.borrow().values.clone());
229
230 drop(nodes);
231
232 rt.queue(key);
233 }
234 }
235 Err(error) => {
236 let mut nodes = rt.nodes.borrow_mut();
237
238 if let Some(key) = child_key.get() {
239 drop_node(&mut nodes, key);
240 }
241
242 (catch_cx.f)((error.make_error)())
243 }
244 }
245 }
246}
247
248pub(crate) struct CatchContext {
249 f: Rc<dyn Fn(Box<dyn core::error::Error>)>,
250}
251
252impl CatchContext {
253 pub(crate) fn new(f: impl Fn(Box<dyn core::error::Error>) + 'static) -> Self {
254 Self { f: Rc::new(f) }
255 }
256}
257
258macro_rules! impl_tuples {
259 ($($t:tt : $idx:tt),*) => {
260 unsafe impl<$($t: Data),*> Data for ($($t,)*) {}
261
262 impl<$($t: Compose),*> Compose for ($($t,)*) {
263 fn compose(cx: Scope<Self>) -> impl Compose {
264 $({
265 let ptr: *const dyn AnyCompose = unsafe { mem::transmute(&cx.me().$idx as *const dyn AnyCompose) };
266 let (key, _) = use_node(&cx, ComposePtr::Ptr(ptr), $idx);
267
268 let rt = Runtime::current();
269 rt.queue(key)
270 })*
271 }
272
273 fn name() -> Option<Cow<'static, str>> {
274 None
275 }
276 }
277 };
278}
279
280impl_tuples!(T1:0);
281impl_tuples!(T1:0, T2:1);
282impl_tuples!(T1:0, T2:1, T3:2);
283impl_tuples!(T1:0, T2:1, T3:2, T4:3);
284impl_tuples!(T1:0, T2:1, T3:2, T4:3, T5:4);
285impl_tuples!(T1:0, T2:1, T3:2, T4:3, T5:4, T6:5);
286impl_tuples!(T1:0, T2:1, T3:2, T4:3, T5:4, T6:5, T7:6);
287impl_tuples!(T1:0, T2:1, T3:2, T4:3, T5:4, T6:5, T7:6, T8:7);
288
289impl<C> Compose for Vec<C>
290where
291 C: Compose,
292{
293 fn compose(cx: Scope<Self>) -> impl Compose {
294 for (idx, item) in cx.me().iter().enumerate() {
295 let ptr: *const dyn AnyCompose =
296 unsafe { mem::transmute(item as *const dyn AnyCompose) };
297 let (key, _) = use_node(&cx, ComposePtr::Ptr(ptr), idx);
298
299 let rt = Runtime::current();
300 rt.queue(key);
301 }
302 }
303}
304
305fn use_node(
306 cx: ScopeState<'_>,
307 compose_ptr: ComposePtr,
308 child_idx: usize,
309) -> (DefaultKey, &Rc<Node>) {
310 let mut compose_ptr_cell = Some(compose_ptr);
311
312 let (key, node) = use_ref(cx, || {
313 let rt = Runtime::current();
314 let mut nodes = rt.nodes.borrow_mut();
315
316 let key = nodes.insert(Rc::new(Node {
317 compose: RefCell::new(compose_ptr_cell.take().unwrap()),
318 scope: ScopeData::default(),
319 parent: Some(rt.current_key.get()),
320 children: RefCell::new(Vec::new()),
321 child_idx,
322 }));
323
324 nodes
325 .get(rt.current_key.get())
326 .unwrap()
327 .children
328 .borrow_mut()
329 .push(key);
330
331 let child_state = &nodes[key].scope;
332 *child_state.contexts.borrow_mut() = cx.contexts.borrow().clone();
333 child_state
334 .contexts
335 .borrow_mut()
336 .values
337 .extend(cx.child_contexts.borrow().values.clone());
338
339 (key, nodes[key].clone())
340 });
341
342 if let Some(compose_ptr) = compose_ptr_cell.take() {
344 *node.compose.borrow_mut() = compose_ptr;
345 }
346
347 (*key, node)
348}
349
350pub(crate) trait AnyCompose {
351 fn data_id(&self) -> TypeId;
352
353 fn as_ptr_mut(&mut self) -> *mut ();
354
355 unsafe fn reborrow(&mut self, ptr: *mut ());
356
357 unsafe fn any_compose(&self, state: &ScopeData);
359
360 fn name(&self) -> Option<Cow<'static, str>>;
361}
362
363impl<C> AnyCompose for C
364where
365 C: Compose + Data,
366{
367 fn data_id(&self) -> TypeId {
368 typeid::of::<C>()
369 }
370
371 fn as_ptr_mut(&mut self) -> *mut () {
372 self as *mut Self as *mut ()
373 }
374
375 unsafe fn reborrow(&mut self, ptr: *mut ()) {
376 core::ptr::swap(self, ptr as _);
377 }
378
379 unsafe fn any_compose(&self, state: &ScopeData) {
380 state.hook_idx.set(0);
382
383 state.generation.set(state.generation.get() + 1);
385
386 let state: ScopeState = unsafe { mem::transmute(state) };
389 let cx: Scope<'_, C> = Scope { me: self, state };
390 let cx: Scope<'_, C> = unsafe { mem::transmute(cx) };
391
392 let cell: &UnsafeCell<Option<Box<dyn AnyCompose>>> = use_ref(&cx, || UnsafeCell::new(None));
394 let cell = unsafe { &mut *cell.get() };
396
397 let child_key_cell = use_ref(&cx, || Cell::new(None));
398
399 let rt = Runtime::current();
400
401 if cell.is_none() {
402 #[cfg(feature = "tracing")]
403 if let Some(name) = C::name() {
404 tracing::trace!("Compose: {}", name);
405 }
406
407 let child = C::compose(cx);
408
409 if child.data_id() == typeid::of::<()>() {
410 return;
411 }
412
413 let child: Box<dyn AnyCompose> = Box::new(child);
414 let mut child: Box<dyn AnyCompose> = unsafe { mem::transmute(child) };
415
416 let mut nodes = rt.nodes.borrow_mut();
417
418 unsafe {
419 if let Some(key) = child_key_cell.get() {
420 let last = nodes.get_mut(key).unwrap();
421 child.reborrow(last.compose.borrow_mut().as_ptr_mut());
422 } else {
423 let child_key = nodes.insert(Rc::new(Node {
424 compose: RefCell::new(crate::composer::ComposePtr::Boxed(child)),
425 scope: ScopeData::default(),
426 parent: Some(rt.current_key.get()),
427 children: RefCell::new(Vec::new()),
428 child_idx: 0,
429 }));
430 child_key_cell.set(Some(child_key));
431
432 nodes
433 .get(rt.current_key.get())
434 .unwrap()
435 .children
436 .borrow_mut()
437 .push(child_key);
438
439 let child_state = &nodes[child_key].scope;
440
441 *child_state.contexts.borrow_mut() = cx.contexts.borrow().clone();
442 child_state
443 .contexts
444 .borrow_mut()
445 .values
446 .extend(cx.child_contexts.borrow().values.clone());
447 }
448 }
449 }
450
451 if let Some(key) = child_key_cell.get() {
452 rt.queue(key)
453 }
454 }
455
456 fn name(&self) -> Option<Cow<'static, str>> {
457 C::name()
458 }
459}