Skip to main content

allframe_core/
macros.rs

1// ─── Non-generic handler erasure macros ────────────────────────────────────
2//
3// These macros generate **fully concrete** boxing code — no generic function
4// is monomorphized at the call site. This keeps trait-resolution pressure
5// near zero even with hundreds of handlers, preventing E0275 overflow.
6//
7// See ADR-0005 for the design rationale.
8
9/// Erase a **no-args** handler into an [`ErasedHandler`](crate::router::ErasedHandler).
10///
11/// The handler must be an `async fn() -> R` where `R: IntoHandlerResult`.
12///
13/// # Example
14///
15/// ```rust,ignore
16/// use allframe_core::{erase_handler, router::Router};
17///
18/// #[allframe_macros::allframe_handler]
19/// async fn health() -> String { "ok".into() }
20///
21/// let mut router = Router::new();
22/// router.register_erased("health", erase_handler!(health));
23/// ```
24#[macro_export]
25macro_rules! erase_handler {
26    ($handler:expr) => {
27        $crate::router::ErasedHandler::from_closure(Box::new(move |_args: &str| {
28            let fut = $handler();
29            Box::pin(async move {
30                <_ as $crate::router::IntoHandlerResult>::into_handler_result(fut.await)
31            })
32                as ::std::pin::Pin<
33                    Box<dyn ::std::future::Future<Output = ::std::result::Result<String, String>> + Send>,
34                >
35        }))
36    };
37}
38
39/// Erase a handler **with typed args** into an [`ErasedHandler`](crate::router::ErasedHandler).
40///
41/// The handler must be an `async fn(T) -> R` where `T: DeserializeOwned` and
42/// `R: IntoHandlerResult`.
43///
44/// # Example
45///
46/// ```rust,ignore
47/// use allframe_core::{erase_handler_with_args, router::Router};
48///
49/// #[allframe_macros::allframe_handler]
50/// async fn get_user(args: GetUserArgs) -> Result<User, MyError> { todo!() }
51///
52/// let mut router = Router::new();
53/// router.register_erased(
54///     "get_user",
55///     erase_handler_with_args!(get_user, GetUserArgs),
56/// );
57/// ```
58#[macro_export]
59macro_rules! erase_handler_with_args {
60    ($handler:expr, $args_ty:ty) => {
61        $crate::router::ErasedHandler::from_closure(Box::new(move |args_str: &str| {
62            let parsed: ::std::result::Result<$args_ty, _> = ::serde_json::from_str(args_str);
63            match parsed {
64                Ok(value) => {
65                    let fut = $handler(value);
66                    Box::pin(async move {
67                        <_ as $crate::router::IntoHandlerResult>::into_handler_result(fut.await)
68                    })
69                        as ::std::pin::Pin<
70                            Box<
71                                dyn ::std::future::Future<
72                                        Output = ::std::result::Result<String, String>,
73                                    > + Send,
74                            >,
75                        >
76                }
77                Err(e) => Box::pin(async move {
78                    Err(format!("Failed to deserialize args: {e}"))
79                })
80                    as ::std::pin::Pin<
81                        Box<
82                            dyn ::std::future::Future<
83                                    Output = ::std::result::Result<String, String>,
84                                > + Send,
85                        >,
86                    >,
87            }
88        }))
89    };
90}
91
92/// Erase a handler **with state + typed args** into an [`ErasedHandler`](crate::router::ErasedHandler).
93///
94/// The handler must be an `async fn(State<Arc<S>>, T) -> R`.
95///
96/// # Example
97///
98/// ```rust,ignore
99/// use allframe_core::{erase_handler_with_state, router::Router};
100///
101/// #[allframe_macros::allframe_handler]
102/// async fn get_user(
103///     state: State<Arc<AppState>>,
104///     args: GetUserArgs,
105/// ) -> Result<User, MyError> { todo!() }
106///
107/// let mut router = Router::new();
108/// let states = router.shared_states();
109/// router.register_erased(
110///     "get_user",
111///     erase_handler_with_state!(get_user, AppState, GetUserArgs, states),
112/// );
113/// ```
114#[macro_export]
115macro_rules! erase_handler_with_state {
116    ($handler:expr, $state_ty:ty, $args_ty:ty, $states:expr) => {{
117        let states = $states;
118        let __af_type_id = ::std::any::TypeId::of::<$state_ty>();
119        let __af_type_name = ::std::any::type_name::<$state_ty>();
120        $crate::router::ErasedHandler::from_closure(Box::new(move |args_str: &str| {
121            let state_arc: ::std::result::Result<::std::sync::Arc<$state_ty>, String> =
122                $crate::router::resolve_state_erased(&states, __af_type_id, __af_type_name)
123                    .and_then(|any| {
124                        any.downcast::<$state_ty>().map_err(|_| {
125                            format!("State type mismatch: expected {}", __af_type_name)
126                        })
127                    });
128            match state_arc {
129                Ok(s) => {
130                    let parsed: ::std::result::Result<$args_ty, _> =
131                        ::serde_json::from_str(args_str);
132                    match parsed {
133                        Ok(value) => {
134                            let fut = $handler($crate::router::State(s), value);
135                            Box::pin(async move {
136                                <_ as $crate::router::IntoHandlerResult>::into_handler_result(
137                                    fut.await,
138                                )
139                            })
140                                as ::std::pin::Pin<
141                                    Box<
142                                        dyn ::std::future::Future<
143                                                Output = ::std::result::Result<String, String>,
144                                            > + Send,
145                                    >,
146                                >
147                        }
148                        Err(e) => Box::pin(async move {
149                            Err(format!("Failed to deserialize args: {e}"))
150                        })
151                            as ::std::pin::Pin<
152                                Box<
153                                    dyn ::std::future::Future<
154                                            Output = ::std::result::Result<String, String>,
155                                        > + Send,
156                                    >,
157                                >,
158                    }
159                }
160                Err(msg) => {
161                    Box::pin(async move { Err(msg) })
162                        as ::std::pin::Pin<
163                            Box<
164                                dyn ::std::future::Future<
165                                        Output = ::std::result::Result<String, String>,
166                                    > + Send,
167                            >,
168                        >
169                }
170            }
171        }))
172    }};
173}
174
175/// Erase a **state-only** handler into an [`ErasedHandler`](crate::router::ErasedHandler).
176///
177/// The handler must be an `async fn(State<Arc<S>>) -> R`.
178///
179/// # Example
180///
181/// ```rust,ignore
182/// use allframe_core::{erase_handler_with_state_only, router::Router};
183///
184/// #[allframe_macros::allframe_handler]
185/// async fn db_status(state: State<Arc<AppState>>) -> String { todo!() }
186///
187/// let mut router = Router::new();
188/// let states = router.shared_states();
189/// router.register_erased(
190///     "db_status",
191///     erase_handler_with_state_only!(db_status, AppState, states),
192/// );
193/// ```
194#[macro_export]
195macro_rules! erase_handler_with_state_only {
196    ($handler:expr, $state_ty:ty, $states:expr) => {{
197        let states = $states;
198        let __af_type_id = ::std::any::TypeId::of::<$state_ty>();
199        let __af_type_name = ::std::any::type_name::<$state_ty>();
200        $crate::router::ErasedHandler::from_closure(Box::new(move |_args: &str| {
201            let state_arc: ::std::result::Result<::std::sync::Arc<$state_ty>, String> =
202                $crate::router::resolve_state_erased(&states, __af_type_id, __af_type_name)
203                    .and_then(|any| {
204                        any.downcast::<$state_ty>().map_err(|_| {
205                            format!("State type mismatch: expected {}", __af_type_name)
206                        })
207                    });
208            match state_arc {
209                Ok(s) => {
210                    let fut = $handler($crate::router::State(s));
211                    Box::pin(async move {
212                        <_ as $crate::router::IntoHandlerResult>::into_handler_result(fut.await)
213                    })
214                        as ::std::pin::Pin<
215                            Box<
216                                dyn ::std::future::Future<
217                                        Output = ::std::result::Result<String, String>,
218                                    > + Send,
219                            >,
220                        >
221                }
222                Err(msg) => {
223                    Box::pin(async move { Err(msg) })
224                        as ::std::pin::Pin<
225                            Box<
226                                dyn ::std::future::Future<
227                                        Output = ::std::result::Result<String, String>,
228                                    > + Send,
229                            >,
230                        >
231                }
232            }
233        }))
234    }};
235}
236
237// ─── Streaming handler erasure macros ──────────────────────────────────────
238
239/// Erase a **no-args streaming** handler into an [`ErasedStreamHandler`](crate::router::ErasedStreamHandler).
240///
241/// The handler must be an `async fn(StreamSender) -> R`.
242///
243/// # Example
244///
245/// ```rust,ignore
246/// use allframe_core::{erase_streaming_handler, router::{Router, StreamSender}};
247///
248/// #[allframe_macros::allframe_handler(streaming)]
249/// async fn stream_data(tx: StreamSender) -> String { "done".into() }
250///
251/// let mut router = Router::new();
252/// router.register_streaming_erased("stream_data", erase_streaming_handler!(stream_data));
253/// ```
254#[macro_export]
255macro_rules! erase_streaming_handler {
256    ($handler:expr) => {
257        $crate::router::ErasedStreamHandler::from_closure(Box::new(
258            move |_args: &str, tx: $crate::router::StreamSender| {
259                let fut = $handler(tx);
260                Box::pin(async move {
261                    <_ as $crate::router::IntoHandlerResult>::into_handler_result(fut.await)
262                })
263                    as ::std::pin::Pin<
264                        Box<
265                            dyn ::std::future::Future<
266                                    Output = ::std::result::Result<String, String>,
267                                > + Send,
268                        >,
269                    >
270            },
271        ))
272    };
273}
274
275/// Erase a streaming handler **with typed args** into an [`ErasedStreamHandler`](crate::router::ErasedStreamHandler).
276///
277/// The handler must be an `async fn(T, StreamSender) -> R`.
278///
279/// # Example
280///
281/// ```rust,ignore
282/// use allframe_core::{erase_streaming_handler_with_args, router::{Router, StreamSender}};
283///
284/// #[allframe_macros::allframe_handler(streaming)]
285/// async fn stream_user(args: GetUserArgs, tx: StreamSender) -> String { "done".into() }
286///
287/// let mut router = Router::new();
288/// router.register_streaming_erased(
289///     "stream_user",
290///     erase_streaming_handler_with_args!(stream_user, GetUserArgs),
291/// );
292/// ```
293#[macro_export]
294macro_rules! erase_streaming_handler_with_args {
295    ($handler:expr, $args_ty:ty) => {
296        $crate::router::ErasedStreamHandler::from_closure(Box::new(
297            move |args_str: &str, tx: $crate::router::StreamSender| {
298                let parsed: ::std::result::Result<$args_ty, _> = ::serde_json::from_str(args_str);
299                match parsed {
300                    Ok(value) => {
301                        let fut = $handler(value, tx);
302                        Box::pin(async move {
303                            <_ as $crate::router::IntoHandlerResult>::into_handler_result(
304                                fut.await,
305                            )
306                        })
307                            as ::std::pin::Pin<
308                                Box<
309                                    dyn ::std::future::Future<
310                                            Output = ::std::result::Result<String, String>,
311                                        > + Send,
312                                >,
313                            >
314                    }
315                    Err(e) => Box::pin(async move {
316                        Err(format!("Failed to deserialize args: {e}"))
317                    })
318                        as ::std::pin::Pin<
319                            Box<
320                                dyn ::std::future::Future<
321                                        Output = ::std::result::Result<String, String>,
322                                    > + Send,
323                                >,
324                            >,
325                }
326            },
327        ))
328    };
329}
330
331/// Erase a streaming handler **with state + typed args** into an [`ErasedStreamHandler`](crate::router::ErasedStreamHandler).
332///
333/// The handler must be an `async fn(State<Arc<S>>, T, StreamSender) -> R`.
334#[macro_export]
335macro_rules! erase_streaming_handler_with_state {
336    ($handler:expr, $state_ty:ty, $args_ty:ty, $states:expr) => {{
337        let states = $states;
338        let __af_type_id = ::std::any::TypeId::of::<$state_ty>();
339        let __af_type_name = ::std::any::type_name::<$state_ty>();
340        $crate::router::ErasedStreamHandler::from_closure(Box::new(
341            move |args_str: &str, tx: $crate::router::StreamSender| {
342                let state_arc: ::std::result::Result<::std::sync::Arc<$state_ty>, String> =
343                    $crate::router::resolve_state_erased(&states, __af_type_id, __af_type_name)
344                        .and_then(|any| {
345                            any.downcast::<$state_ty>().map_err(|_| {
346                                format!("State type mismatch: expected {}", __af_type_name)
347                            })
348                        });
349                match state_arc {
350                    Ok(s) => {
351                        let parsed: ::std::result::Result<$args_ty, _> =
352                            ::serde_json::from_str(args_str);
353                        match parsed {
354                            Ok(value) => {
355                                let fut = $handler($crate::router::State(s), value, tx);
356                                Box::pin(async move {
357                                    <_ as $crate::router::IntoHandlerResult>::into_handler_result(
358                                        fut.await,
359                                    )
360                                })
361                                    as ::std::pin::Pin<
362                                        Box<
363                                            dyn ::std::future::Future<
364                                                    Output = ::std::result::Result<String, String>,
365                                                > + Send,
366                                        >,
367                                    >
368                            }
369                            Err(e) => Box::pin(async move {
370                                Err(format!("Failed to deserialize args: {e}"))
371                            })
372                                as ::std::pin::Pin<
373                                    Box<
374                                        dyn ::std::future::Future<
375                                                Output = ::std::result::Result<String, String>,
376                                            > + Send,
377                                    >,
378                                >,
379                        }
380                    }
381                    Err(msg) => {
382                        Box::pin(async move { Err(msg) })
383                            as ::std::pin::Pin<
384                                Box<
385                                    dyn ::std::future::Future<
386                                            Output = ::std::result::Result<String, String>,
387                                        > + Send,
388                                >,
389                            >
390                    }
391                }
392            },
393        ))
394    }};
395}
396
397/// Erase a streaming handler **with state only** into an [`ErasedStreamHandler`](crate::router::ErasedStreamHandler).
398///
399/// The handler must be an `async fn(State<Arc<S>>, StreamSender) -> R`.
400#[macro_export]
401macro_rules! erase_streaming_handler_with_state_only {
402    ($handler:expr, $state_ty:ty, $states:expr) => {{
403        let states = $states;
404        let __af_type_id = ::std::any::TypeId::of::<$state_ty>();
405        let __af_type_name = ::std::any::type_name::<$state_ty>();
406        $crate::router::ErasedStreamHandler::from_closure(Box::new(
407            move |_args: &str, tx: $crate::router::StreamSender| {
408                let state_arc: ::std::result::Result<::std::sync::Arc<$state_ty>, String> =
409                    $crate::router::resolve_state_erased(&states, __af_type_id, __af_type_name)
410                        .and_then(|any| {
411                            any.downcast::<$state_ty>().map_err(|_| {
412                                format!("State type mismatch: expected {}", __af_type_name)
413                            })
414                        });
415                match state_arc {
416                    Ok(s) => {
417                        let fut = $handler($crate::router::State(s), tx);
418                        Box::pin(async move {
419                            <_ as $crate::router::IntoHandlerResult>::into_handler_result(
420                                fut.await,
421                            )
422                        })
423                            as ::std::pin::Pin<
424                                Box<
425                                    dyn ::std::future::Future<
426                                            Output = ::std::result::Result<String, String>,
427                                        > + Send,
428                                >,
429                            >
430                    }
431                    Err(msg) => {
432                        Box::pin(async move { Err(msg) })
433                            as ::std::pin::Pin<
434                                Box<
435                                    dyn ::std::future::Future<
436                                            Output = ::std::result::Result<String, String>,
437                                        > + Send,
438                                    >,
439                                >
440                    }
441                }
442            },
443        ))
444    }};
445}
446
447// ─── Batch registration macro ──────────────────────────────────────────────
448
449/// Register multiple handlers at once using the **non-generic** erased path.
450///
451/// This is the recommended way to register large numbers of handlers.
452/// Each entry generates **zero** generic monomorphizations, preventing
453/// E0275 overflow regardless of handler count.
454///
455/// Compared to [`register_handlers!`] (which uses the generic registration
456/// methods), this macro requires explicit type annotations but produces no
457/// trait-resolution pressure.
458///
459/// # Syntax
460///
461/// ```rust,ignore
462/// use allframe_core::{register_handlers_erased, router::Router};
463///
464/// let mut router = Router::new()
465///     .with_state(app_state);
466///
467/// register_handlers_erased!(router, {
468///     // No args
469///     "health"      => health(),
470///
471///     // With typed args
472///     "get_user"    => get_user(args: GetUserArgs),
473///     "create_user" => create_user(args: CreateUserArgs),
474///
475///     // With state + args
476///     "update_user" => update_user(state: AppState, args: UpdateArgs),
477///
478///     // State only
479///     "db_status"   => db_status(state: AppState),
480///
481///     // Streaming (no args)
482///     "events"      => stream events_stream(),
483///
484///     // Streaming with args
485///     "user_feed"   => stream user_feed(args: FeedArgs),
486///
487///     // Streaming with state + args
488///     "live_query"  => stream live_query(state: AppState, args: QueryArgs),
489///
490///     // Streaming with state only
491///     "heartbeat"   => stream heartbeat(state: AppState),
492/// });
493/// ```
494#[macro_export]
495macro_rules! register_handlers_erased {
496    ($router:expr, { $($entries:tt)* }) => {
497        #[allow(unused_variables)]
498        let states = $router.shared_states();
499        $crate::__rhe_entries!($router, states, $($entries)*);
500    };
501}
502
503/// Internal recursive TT muncher — do not use directly.
504#[macro_export]
505#[doc(hidden)]
506macro_rules! __rhe_entries {
507    // ── Terminal case ──────────────────────────────────────────────────
508    ($router:expr, $states:expr, ) => {};
509
510    // ── Streaming with state + args ────────────────────────────────────
511    ($router:expr, $states:expr, $name:literal => stream $handler:ident (state: $sty:ty, args: $aty:ty), $($rest:tt)*) => {
512        $router.register_streaming_erased(
513            $name,
514            $crate::erase_streaming_handler_with_state!($handler, $sty, $aty, $states.clone()),
515        );
516        $crate::__rhe_entries!($router, $states, $($rest)*)
517    };
518    // ── Streaming with state only ──────────────────────────────────────
519    ($router:expr, $states:expr, $name:literal => stream $handler:ident (state: $sty:ty), $($rest:tt)*) => {
520        $router.register_streaming_erased(
521            $name,
522            $crate::erase_streaming_handler_with_state_only!($handler, $sty, $states.clone()),
523        );
524        $crate::__rhe_entries!($router, $states, $($rest)*)
525    };
526    // ── Streaming with args ────────────────────────────────────────────
527    ($router:expr, $states:expr, $name:literal => stream $handler:ident (args: $aty:ty), $($rest:tt)*) => {
528        $router.register_streaming_erased(
529            $name,
530            $crate::erase_streaming_handler_with_args!($handler, $aty),
531        );
532        $crate::__rhe_entries!($router, $states, $($rest)*)
533    };
534    // ── Streaming no args ──────────────────────────────────────────────
535    ($router:expr, $states:expr, $name:literal => stream $handler:ident (), $($rest:tt)*) => {
536        $router.register_streaming_erased(
537            $name,
538            $crate::erase_streaming_handler!($handler),
539        );
540        $crate::__rhe_entries!($router, $states, $($rest)*)
541    };
542    // ── Request/response with state + args ─────────────────────────────
543    ($router:expr, $states:expr, $name:literal => $handler:ident (state: $sty:ty, args: $aty:ty), $($rest:tt)*) => {
544        $router.register_erased(
545            $name,
546            $crate::erase_handler_with_state!($handler, $sty, $aty, $states.clone()),
547        );
548        $crate::__rhe_entries!($router, $states, $($rest)*)
549    };
550    // ── Request/response with state only ───────────────────────────────
551    ($router:expr, $states:expr, $name:literal => $handler:ident (state: $sty:ty), $($rest:tt)*) => {
552        $router.register_erased(
553            $name,
554            $crate::erase_handler_with_state_only!($handler, $sty, $states.clone()),
555        );
556        $crate::__rhe_entries!($router, $states, $($rest)*)
557    };
558    // ── Request/response with args ─────────────────────────────────────
559    ($router:expr, $states:expr, $name:literal => $handler:ident (args: $aty:ty), $($rest:tt)*) => {
560        $router.register_erased(
561            $name,
562            $crate::erase_handler_with_args!($handler, $aty),
563        );
564        $crate::__rhe_entries!($router, $states, $($rest)*)
565    };
566    // ── Request/response no args ───────────────────────────────────────
567    ($router:expr, $states:expr, $name:literal => $handler:ident (), $($rest:tt)*) => {
568        $router.register_erased(
569            $name,
570            $crate::erase_handler!($handler),
571        );
572        $crate::__rhe_entries!($router, $states, $($rest)*)
573    };
574}