1use crate::extract::{ExtractionState, Extractor};
7use crate::{AnyView, View};
8use alloc::boxed::Box;
9use alloc::rc::Rc;
10use core::any::type_name;
11use core::cell::RefCell;
12use core::fmt;
13
14use crate::Environment;
15
16pub type BoxedAction<T = ()> = Box<dyn FnMut(&Environment) -> T>;
20
21pub type BoxedActionOnce<T = ()> = Box<dyn FnOnce(&Environment) -> T>;
25
26pub type ActionObject = BoxedAction<()>;
28
29fn extract_or_panic<T: Extractor>(env: &Environment, state: &mut ExtractionState) -> T {
30 T::extract_from_action(env, state).unwrap_or_else(|error| {
31 panic!(
32 "failed to extract `{}` from environment for action: {error}",
33 type_name::<T>()
34 )
35 })
36}
37
38pub trait Handler<Args, T = ()>: 'static {
40 fn call(&mut self, env: &Environment) -> T;
42}
43
44pub trait HandlerOnce<Args, T = ()>: 'static {
46 fn call_once(self, env: &Environment) -> T;
48}
49
50macro_rules! impl_handler {
51 () => {
52 impl<F, Output> Handler<(), Output> for F
53 where
54 F: FnMut() -> Output + 'static,
55 {
56 fn call(&mut self, _env: &Environment) -> Output {
57 self()
58 }
59 }
60
61 impl<F, Output> HandlerOnce<(), Output> for F
62 where
63 F: FnOnce() -> Output + 'static,
64 {
65 fn call_once(self, _env: &Environment) -> Output {
66 self()
67 }
68 }
69 };
70 ($($T:ident),+) => {
71 impl<Func, Output, $($T),+> Handler<($($T,)+), Output> for Func
72 where
73 Func: FnMut($($T),+) -> Output + 'static,
74 $($T: Extractor),+
75 {
76 #[allow(non_snake_case)]
77 fn call(&mut self, env: &Environment) -> Output {
78 let mut state = ExtractionState::default();
79 $(let $T = extract_or_panic::<$T>(env, &mut state);)+
80 self($($T),+)
81 }
82 }
83
84 impl<Func, Output, $($T),+> HandlerOnce<($($T,)+), Output> for Func
85 where
86 Func: FnOnce($($T),+) -> Output + 'static,
87 $($T: Extractor),+
88 {
89 #[allow(non_snake_case)]
90 fn call_once(self, env: &Environment) -> Output {
91 let mut state = ExtractionState::default();
92 $(let $T = extract_or_panic::<$T>(env, &mut state);)+
93 self($($T),+)
94 }
95 }
96 };
97}
98
99impl_handler!();
100impl_handler!(A);
101impl_handler!(A, B);
102impl_handler!(A, B, C);
103impl_handler!(A, B, C, D);
104impl_handler!(A, B, C, D, E);
105impl_handler!(A, B, C, D, E, F);
106impl_handler!(A, B, C, D, E, F, G);
107impl_handler!(A, B, C, D, E, F, G, H);
108
109#[inline]
111pub fn boxed_action<Args, T: 'static>(mut f: impl Handler<Args, T>) -> BoxedAction<T> {
112 Box::new(move |env: &Environment| f.call(env))
113}
114
115#[inline]
117pub fn boxed_action_once<Args, T: 'static>(f: impl HandlerOnce<Args, T>) -> BoxedActionOnce<T> {
118 Box::new(move |env: &Environment| f.call_once(env))
119}
120
121pub type BoxedEventAction<E, T = ()> = Box<dyn FnMut(E, &Environment) -> T>;
135
136pub trait EventHandler<E, Args, T = ()>: 'static {
150 fn call(&mut self, event: E, env: &Environment) -> T;
153}
154
155macro_rules! impl_event_handler {
156 () => {
157 impl<F, E, Output> EventHandler<E, (), Output> for F
158 where
159 F: FnMut(E) -> Output + 'static,
160 {
161 fn call(&mut self, event: E, _env: &Environment) -> Output {
162 self(event)
163 }
164 }
165 };
166 ($($T:ident),+) => {
167 impl<Func, E, Output, $($T),+> EventHandler<E, ($($T,)+), Output> for Func
168 where
169 Func: FnMut(E, $($T),+) -> Output + 'static,
170 $($T: Extractor),+
171 {
172 #[allow(non_snake_case)]
173 fn call(&mut self, event: E, env: &Environment) -> Output {
174 let mut state = ExtractionState::default();
175 $(let $T = extract_or_panic::<$T>(env, &mut state);)+
176 self(event, $($T),+)
177 }
178 }
179 };
180}
181
182impl_event_handler!();
183impl_event_handler!(A);
184impl_event_handler!(A, B);
185impl_event_handler!(A, B, C);
186impl_event_handler!(A, B, C, D);
187impl_event_handler!(A, B, C, D, E1);
188impl_event_handler!(A, B, C, D, E1, F);
189impl_event_handler!(A, B, C, D, E1, F, G);
190impl_event_handler!(A, B, C, D, E1, F, G, H);
191
192#[inline]
196pub fn boxed_event_handler<E, Args, T: 'static>(
197 mut f: impl EventHandler<E, Args, T>,
198) -> BoxedEventAction<E, T>
199where
200 E: 'static,
201{
202 Box::new(move |event: E, env: &Environment| f.call(event, env))
203}
204
205type SharedActionFn<T> = Rc<RefCell<Box<dyn FnMut(&Environment) -> T>>>;
214
215#[derive(Clone)]
217pub struct SharedAction<T = ()>(SharedActionFn<T>);
218
219impl<T: 'static> SharedAction<T> {
220 pub fn new<Args>(f: impl Handler<Args, T>) -> Self {
222 Self(Rc::new(RefCell::new(boxed_action(f))))
223 }
224
225 #[expect(
227 clippy::must_use_candidate,
228 reason = "actions are side-effectful and may intentionally return unit"
229 )]
230 pub fn call(&self, env: &Environment) -> T {
231 (self.0.borrow_mut())(env)
232 }
233}
234
235impl<T> fmt::Debug for SharedAction<T> {
236 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
237 f.write_str("SharedAction")
238 }
239}
240
241#[inline]
243pub fn shared_action<Args, T: 'static>(f: impl Handler<Args, T>) -> SharedAction<T> {
244 SharedAction::new(f)
245}
246
247pub trait ViewBuilder: 'static {
256 type Output: View;
258 fn build(&self) -> Self::Output;
260}
261
262impl<V: View, F> ViewBuilder for F
263where
264 F: 'static + Fn() -> V,
265{
266 type Output = V;
267 fn build(&self) -> Self::Output {
268 (self)()
269 }
270}
271
272pub struct AnyViewBuilder<V = AnyView>(Rc<dyn ViewBuilder<Output = V>>);
274
275impl<V> Clone for AnyViewBuilder<V> {
276 fn clone(&self) -> Self {
277 Self(Rc::clone(&self.0))
278 }
279}
280
281impl<V: View> AnyViewBuilder<V> {
282 #[must_use]
284 pub fn new(handler: impl ViewBuilder<Output = V>) -> Self {
285 Self(Rc::new(handler))
286 }
287
288 #[must_use]
290 pub fn build(&self) -> V {
291 ViewBuilder::build(&*self.0)
292 }
293
294 #[must_use]
296 pub fn erase(self) -> AnyViewBuilder<AnyView> {
297 AnyViewBuilder::new(move || {
298 let v = self.build();
299 AnyView::new(v)
300 })
301 }
302}
303
304impl<V> fmt::Debug for AnyViewBuilder<V> {
305 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
306 f.write_str("AnyViewBuilder")
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::*;
313 use alloc::rc::Rc;
314 use core::cell::Cell;
315
316 #[test]
317 fn shared_action_invokes_unit_handler_repeatedly() {
318 let count = Rc::new(Cell::new(0));
319 let captured_count = Rc::clone(&count);
320 let action = shared_action(move || captured_count.set(captured_count.get() + 1));
321
322 action.call(&Environment::default());
323 action.call(&Environment::default());
324
325 assert_eq!(count.get(), 2);
326 }
327
328 #[test]
329 fn shared_action_preserves_return_values() {
330 let action = shared_action(|| 7);
331
332 assert_eq!(action.call(&Environment::default()), 7);
333 }
334}