From 30151b6254d9c3f22a055088cd4d63b73b28776b Mon Sep 17 00:00:00 2001 From: Manuel Cillero Date: Tue, 14 Jul 2026 00:44:21 +0200 Subject: [PATCH] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(macros):=20Deja=20main/te?= =?UTF-8?q?st=20sin=20depender=20de=20Tokio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Antes delegaban directamente en `#[tokio::main]`/`#[tokio::test]`; ahora construyen su propio runtime vía `pagetop::util::build_runtime()`, con el mismo comportamiento (`Builder::new_multi_thread().enable_all()`). Las extensiones que usan estas macros ya no necesitan declarar Tokio como dependencia directa en su `Cargo.toml`. --- helpers/pagetop-macros/src/lib.rs | 61 ++++++++++++++++++++++--------- src/util.rs | 14 +++++++ 2 files changed, 58 insertions(+), 17 deletions(-) diff --git a/helpers/pagetop-macros/src/lib.rs b/helpers/pagetop-macros/src/lib.rs index 4557196e..0fa1fd71 100644 --- a/helpers/pagetop-macros/src/lib.rs +++ b/helpers/pagetop-macros/src/lib.rs @@ -39,7 +39,7 @@ mod smart_default; use proc_macro::TokenStream; use quote::{quote, quote_spanned}; -use syn::{DeriveInput, parse_macro_input, spanned::Spanned}; +use syn::{DeriveInput, ItemFn, parse_macro_input, spanned::Spanned}; /// Macro para escribir plantillas HTML (basada en [Maud](https://docs.rs/maud)). #[proc_macro] @@ -450,19 +450,14 @@ pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { - let mut output: TokenStream = (quote! { - #[::tokio::main] - }) - .into(); - - output.extend(item); - output + let input = parse_macro_input!(item as ItemFn); + expand_entry(input, false) } /// Define funciones de prueba asíncronas para usar con PageTop. /// -/// Usa el *runtime* multi-hilo de **Tokio**, igual que [`#[pagetop::main]`](macro@main), para -/// garantizar compatibilidad con extensiones que ejecutan código asíncrono de forma síncrona. +/// Usa el mismo *runtime* multi-hilo que [`#[pagetop::main]`](macro@main), para garantizar +/// compatibilidad con extensiones que ejecutan código asíncrono de forma síncrona. /// /// # Ejemplo /// @@ -474,11 +469,43 @@ pub fn main(_: TokenStream, item: TokenStream) -> TokenStream { /// ``` #[proc_macro_attribute] pub fn test(_: TokenStream, item: TokenStream) -> TokenStream { - let mut output: TokenStream = (quote! { - #[::tokio::test(flavor = "multi_thread")] - }) - .into(); - - output.extend(item); - output + let input = parse_macro_input!(item as ItemFn); + expand_entry(input, true) +} + +// Genera la función síncrona que envuelve el cuerpo asíncrono original, común a `main` y `test`. +fn expand_entry(input: ItemFn, is_test: bool) -> TokenStream { + if input.sig.asyncness.is_none() { + return syn::Error::new_spanned(&input.sig.fn_token, "the function must be `async`") + .to_compile_error() + .into(); + } + + let ItemFn { + attrs, + vis, + mut sig, + block, + } = input; + sig.asyncness = None; + + // Ruta absoluta para evitar ambigüedad con `pagetop::test` bajo `use pagetop::prelude::*;`. + let test_attr = is_test.then(|| quote! { #[::core::prelude::v1::test] }); + + let expanded = quote! { + #test_attr + #(#attrs)* + #vis #sig { + #[allow( + clippy::expect_used, + clippy::diverging_sub_expression, + clippy::needless_return, + clippy::unwrap_in_result + )] + { + return ::pagetop::util::build_runtime().block_on(async move #block); + } + } + }; + expanded.into() } diff --git a/src/util.rs b/src/util.rs index 5c1fff49..bedee08e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -25,6 +25,20 @@ pub use pagetop_minimal::paste; // **< FUNCIONES ÚTILES >*************************************************************************** +/// Código requerido por las macros [`pagetop::main`](crate::main) y [`pagetop::test`](crate::test). +/// +/// Equivale a `Builder::new_multi_thread().enable_all().build()`, el mismo runtime que construye +/// `#[tokio::main]`/`#[tokio::test(flavor = "multi_thread")]` sin argumentos. Las aplicaciones o +/// extensiones que usen estas macros no necesitan declarar `tokio` como dependencia directa en su +/// `Cargo.toml`. +/// +/// Esta función no forma parte de la API pública; es `pub` para ser llamada por el código generado +/// por las macros. +#[doc(hidden)] +pub fn build_runtime() -> tokio::runtime::Runtime { + tokio::runtime::Runtime::new().expect("Failed to build the Tokio runtime") +} + /// Errores posibles al normalizar una cadena ASCII con [`normalize_ascii()`]. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum NormalizeAsciiError {