📝 Mejora documentación generada por builder_fn

This commit is contained in:
Manuel Cillero 2025-11-23 14:11:13 +01:00
parent f0e5f50a7f
commit 9657672ffd

View file

@ -138,7 +138,7 @@ pub fn derive_auto_default(input: TokenStream) -> TokenStream {
/// # } /// # }
/// ``` /// ```
/// ///
/// la macro rescribirá el método `with_` y generará un nuevo método `alter_`: /// la macro reescribirá el método `with_` y generará un nuevo método `alter_`:
/// ///
/// ```rust /// ```rust
/// # struct Example {value: Option<String>}; /// # struct Example {value: Option<String>};
@ -157,7 +157,11 @@ pub fn derive_auto_default(input: TokenStream) -> TokenStream {
/// ``` /// ```
/// ///
/// De esta forma, cada método *builder* `with_...()` generará automáticamente su correspondiente /// De esta forma, cada método *builder* `with_...()` generará automáticamente su correspondiente
/// método `alter_...()` para dejar modificar instancias existentes. /// método `alter_...()` para modificar instancias existentes.
///
/// La documentación del método `with_...()` incluirá también la firma resumida del método
/// `alter_...()` y un alias de búsqueda con su nombre, de tal manera que buscando `alter_...` en la
/// documentación se mostrará la entrada del método `with_...()`.
#[proc_macro_attribute] #[proc_macro_attribute]
pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream { pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream {
use syn::{parse2, FnArg, Ident, ImplItemFn, Pat, ReturnType, TraitItemFn, Type}; use syn::{parse2, FnArg, Ident, ImplItemFn, Pat, ReturnType, TraitItemFn, Type};
@ -282,11 +286,11 @@ pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream {
} }
} }
// Genera el nombre del método alter_...(). // Genera el nombre del método `alter_...()`.
let stem = with_name_str.strip_prefix("with_").expect("validated"); let stem = with_name_str.strip_prefix("with_").expect("validated");
let alter_ident = Ident::new(&format!("alter_{stem}"), with_name.span()); let alter_ident = Ident::new(&format!("alter_{stem}"), with_name.span());
// Extrae genéricos y cláusulas where. // Extrae genéricos y cláusulas `where`.
let generics = &sig.generics; let generics = &sig.generics;
let where_clause = &sig.generics.where_clause; let where_clause = &sig.generics.where_clause;
@ -319,28 +323,70 @@ pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream {
v v
}; };
// Filtra los atributos descartando `#[doc]` y `#[inline]` para el método `alter_...()`. // Separa atributos de documentación y resto.
let non_doc_or_inline_attrs: Vec<_> = attrs let mut doc_attrs = Vec::new();
.iter() let mut other_attrs = Vec::new();
.filter(|a| { let mut non_doc_or_inline_attrs = Vec::new();
let p = a.path();
!p.is_ident("doc") && !p.is_ident("inline")
})
.cloned()
.collect();
// Documentación del método alter_...(). for a in attrs.iter() {
let doc = format!("Equivale a [`Self::{with_name_str}()`], pero fuera del patrón *builder*."); let p = a.path();
if p.is_ident("doc") {
doc_attrs.push(a.clone());
} else {
other_attrs.push(a.clone());
if !p.is_ident("inline") {
non_doc_or_inline_attrs.push(a.clone());
}
}
}
// Firma resumida de la función `alter_...()` para mostrarla en la doc de `with_...()`.
let alter_sig_tokens = if args.is_empty() {
// Sin argumentos sólo se muestra `&mut self` (puede que no tenga mucho sentido).
quote! { #vis_pub fn #alter_ident #generics (&mut self) -> &mut Self #where_clause }
} else {
// Con argumentos se muestra `&mut self, ...`.
quote! { #vis_pub fn #alter_ident #generics (&mut self, ...) -> &mut Self #where_clause }
};
// Normaliza espacios raros tipo `& mut`.
let alter_sig_str = alter_sig_tokens.to_string().replace("& mut", "&mut");
// Nombre de la función `alter_...()` como alias de búsqueda.
let alter_name_str = alter_ident.to_string();
// Texto introductorio para la documentación adicional de `with_...()`.
let with_alter_title = format!(
"# Añade método `{}()` generado por [`#[builder_fn]`](pagetop_macros::builder_fn)",
alter_name_str
);
let with_alter_doc = concat!(
"Modifica la instancia actual (`&mut self`) con los mismos argumentos ",
"en lugar de consumirla."
);
// Atributos completos que se aplican siempre a `with_...()`.
let with_prefix = quote! {
#(#other_attrs)*
#(#doc_attrs)*
#[doc(alias = #alter_name_str)]
#[doc = ""]
#[doc = #with_alter_title]
#[doc = #with_alter_doc]
#[doc = "```text"]
#[doc = #alter_sig_str]
#[doc = "```"]
};
// Genera el código final. // Genera el código final.
let expanded = match body_opt { let expanded = match body_opt {
None => { None => {
quote! { quote! {
#(#attrs)* #with_prefix
fn #with_name #generics (self, #(#args),*) -> Self #where_clause; fn #with_name #generics (self, #(#args),*) -> Self #where_clause;
#(#non_doc_or_inline_attrs)* #(#non_doc_or_inline_attrs)*
#[doc = #doc] #[doc(hidden)]
fn #alter_ident #generics (&mut self, #(#args),*) -> &mut Self #where_clause; fn #alter_ident #generics (&mut self, #(#args),*) -> &mut Self #where_clause;
} }
} }
@ -351,8 +397,10 @@ pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream {
} else { } else {
quote! { #[inline] } quote! { #[inline] }
}; };
let with_fn = if is_trait { let with_fn = if is_trait {
quote! { quote! {
#with_prefix
#force_inline #force_inline
#vis_pub fn #with_name #generics (self, #(#args),*) -> Self #where_clause { #vis_pub fn #with_name #generics (self, #(#args),*) -> Self #where_clause {
let mut s = self; let mut s = self;
@ -362,6 +410,7 @@ pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream {
} }
} else { } else {
quote! { quote! {
#with_prefix
#force_inline #force_inline
#vis_pub fn #with_name #generics (mut self, #(#args),*) -> Self #where_clause { #vis_pub fn #with_name #generics (mut self, #(#args),*) -> Self #where_clause {
self.#alter_ident(#(#call_idents),*); self.#alter_ident(#(#call_idents),*);
@ -369,12 +418,12 @@ pub fn builder_fn(_: TokenStream, item: TokenStream) -> TokenStream {
} }
} }
}; };
quote! { quote! {
#(#attrs)*
#with_fn #with_fn
#(#non_doc_or_inline_attrs)* #(#non_doc_or_inline_attrs)*
#[doc = #doc] #[doc(hidden)]
#vis_pub fn #alter_ident #generics (&mut self, #(#args),*) -> &mut Self #where_clause { #vis_pub fn #alter_ident #generics (&mut self, #(#args),*) -> &mut Self #where_clause {
#body #body
} }