aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src/renderer.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src/renderer.rs')
-rw-r--r--mingling_macros/src/renderer.rs86
1 files changed, 38 insertions, 48 deletions
diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs
index 880c50f..9683426 100644
--- a/mingling_macros/src/renderer.rs
+++ b/mingling_macros/src/renderer.rs
@@ -1,23 +1,38 @@
use proc_macro::TokenStream;
-use quote::{ToTokens, quote};
+use quote::{quote, ToTokens};
use syn::spanned::Spanned;
-use syn::{ItemFn, ReturnType, Signature, Type, TypePath, parse_macro_input};
+use syn::{parse_macro_input, ItemFn, ReturnType, Signature, Type, TypePath};
use crate::get_global_set;
use crate::res_injection::{extract_args_info, generate_immut_resource_bindings};
-/// Extracts and returns the return type from the function signature (or None for `()` / no return type).
-fn extract_return_type(sig: &Signature) -> Option<syn::Type> {
+/// Validates that the function returns `::mingling::RenderResult`.
+fn validate_render_result_return(sig: &Signature) -> syn::Result<()> {
match &sig.output {
ReturnType::Type(_, ty) => {
+ // Check if the return type is RenderResult
match &**ty {
- // `()` means no custom return type
- Type::Tuple(tuple) if tuple.elems.is_empty() => None,
- // Any other return type is allowed
- custom_ty => Some((*custom_ty).clone()),
+ Type::Path(type_path) => {
+ let segments = &type_path.path.segments;
+ let last_seg = segments.last().map(|s| s.ident.to_string());
+ match last_seg.as_deref() {
+ Some("RenderResult") => Ok(()),
+ _ => Err(syn::Error::new(
+ ty.span(),
+ "Renderer function must return `RenderResult`",
+ )),
+ }
+ }
+ _ => Err(syn::Error::new(
+ ty.span(),
+ "Renderer function must return `RenderResult`",
+ )),
}
}
- ReturnType::Default => None,
+ ReturnType::Default => Err(syn::Error::new(
+ sig.span(),
+ "Renderer function must have a return type `-> RenderResult`",
+ )),
}
}
@@ -44,8 +59,10 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
Err(e) => return e.to_compile_error().into(),
};
- // Validate return type – now returns Some(type) if custom type, None if ()
- let return_type = extract_return_type(&input_fn.sig);
+ // Validate that the function returns RenderResult
+ if let Err(e) = validate_render_result_return(&input_fn.sig) {
+ return e.to_compile_error().into();
+ }
// Get function body statements
let fn_body_stmts: Vec<syn::Stmt> = input_fn.block.stmts.clone();
@@ -76,23 +93,6 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
let immut_resource_stmts = generate_immut_resource_bindings(resources.iter(), program_type);
let mut_resources: Vec<_> = resources.iter().filter(|r| r.is_mut).collect();
- // Determine public return type and the expression to return dummy_r
- let (public_return_type, result_return) = if let Some(custom_ty) = &return_type {
- // User specified a custom return type (e.g. -> String)
- let ret_ty = quote! { #custom_ty };
- let expr = quote! { dummy_r.into() };
- (ret_ty, expr)
- } else {
- // Return type is () — no custom return type specified
- let ret_ty = quote! { () };
- let expr = quote! {
- if !dummy_r.is_empty() {
- ::std::println!("{}", &*dummy_r);
- }
- };
- (ret_ty, expr)
- };
-
let inner_body_with_resources = if has_mut_resources {
let mut wrapped = quote! { #(#fn_body_stmts)* };
for res in mut_resources.iter().rev() {
@@ -110,8 +110,7 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
};
// Build the Renderer::render body with resource injection
- // Renderer::render returns (), output goes through __renderer_inner_result parameter.
- // Resources are injected from the program context here.
+ // The user's body now directly creates and returns a RenderResult.
let render_fn_body = if has_resources {
quote! {
#(#immut_resource_stmts)*
@@ -121,22 +120,13 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
quote! { #inner_body_with_resources }
};
- // Build the original function body
- // The original function preserves the user's signature and return type.
+ // The original function preserves the user's exact signature and body.
// Resource parameters are passed directly by the caller, NOT injected from context.
- let original_fn_body = {
- quote! {
- let mut dummy_r = ::mingling::RenderResult::default();
- {
- let __renderer_inner_result = &mut dummy_r;
- #(#fn_body_stmts)*
- }
- #result_return
- }
- };
-
- // Keep the original function signature unchanged (same params as user wrote)
let original_inputs = input_fn.sig.inputs.clone();
+ let original_return_type = match &input_fn.sig.output {
+ ReturnType::Type(_, ty) => quote! { #ty },
+ ReturnType::Default => unreachable!("Already validated that return type is RenderResult"),
+ };
let expanded = quote! {
#(#fn_attrs)*
@@ -149,15 +139,15 @@ pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
impl ::mingling::Renderer for #struct_name {
type Previous = #previous_type;
- fn render(#prev_param: Self::Previous, __renderer_inner_result: &mut ::mingling::RenderResult) {
+ fn render(#prev_param: Self::Previous) -> ::mingling::RenderResult {
#render_fn_body
}
}
- // Keep the original function for internal use (without r parameter)
+ // Keep the original function unchanged
#(#fn_attrs)*
- #vis fn #fn_name(#original_inputs) -> #public_return_type {
- #original_fn_body
+ #vis fn #fn_name(#original_inputs) -> #original_return_type {
+ #(#fn_body_stmts)*
}
};