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}