diff options
Diffstat (limited to 'mingling_macros/src/renderer.rs')
| -rw-r--r-- | mingling_macros/src/renderer.rs | 276 |
1 files changed, 0 insertions, 276 deletions
diff --git a/mingling_macros/src/renderer.rs b/mingling_macros/src/renderer.rs deleted file mode 100644 index f47511c..0000000 --- a/mingling_macros/src/renderer.rs +++ /dev/null @@ -1,276 +0,0 @@ -use proc_macro::TokenStream; -use quote::{ToTokens, quote}; -use syn::spanned::Spanned; -use syn::{ItemFn, ReturnType, Signature, Type, TypePath, parse_macro_input}; - -use crate::get_global_set; -use crate::res_injection::{extract_args_info, generate_immut_resource_bindings}; - -/// 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 { - 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 => Err(syn::Error::new( - sig.span(), - "Renderer function must have a return type `-> RenderResult`", - )), - } -} - -#[allow(clippy::too_many_lines)] -pub fn renderer_attr(attr: TokenStream, item: TokenStream) -> TokenStream { - // #[renderer] takes no arguments; always use the default program path - let _ = attr; - let program_path = crate::default_program_path(); - let program_type = &program_path; - - // Parse the function item - let input_fn = parse_macro_input!(item as ItemFn); - - // Validate the function is not async - if input_fn.sig.asyncness.is_some() { - return syn::Error::new(input_fn.sig.span(), "Renderer function cannot be async") - .to_compile_error() - .into(); - } - - // Extract the previous type, parameter name, and resource injection params - let (prev_param, previous_type, resources) = match extract_args_info(&input_fn.sig) { - Ok(info) => info, - Err(e) => return e.to_compile_error().into(), - }; - - // 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(); - - // Get function attributes (excluding the renderer attribute) - let mut fn_attrs = input_fn.attrs.clone(); - - // Remove any #[renderer(...)] attributes to avoid infinite recursion - fn_attrs.retain(|attr| !attr.path().is_ident("renderer")); - - // Get function visibility - let vis = &input_fn.vis; - - // Get function name - let fn_name = &input_fn.sig.ident; - - // Generate struct name from function name using pascal_case - let internal_name = format!( - "__internal_renderer_{}", - just_fmt::snake_case!(fn_name.to_string()) - ); - let struct_name = syn::Ident::new(&internal_name, fn_name.span()); - - let has_resources = !resources.is_empty(); - let has_mut_resources = resources.iter().any(|r| r.is_mut); - - // Generate resource bindings for immutable resources - let immut_resource_stmts = generate_immut_resource_bindings(resources.iter(), program_type); - let mut_resources: Vec<_> = resources.iter().filter(|r| r.is_mut).collect(); - - let inner_body_with_resources = if has_mut_resources { - let mut wrapped = quote! { #(#fn_body_stmts)* }; - for res in mut_resources.iter().rev() { - let var_name = &res.var_name; - let inner_type = &res.inner_type; - wrapped = quote! { - ::mingling::this::<#program_type>().modify_res(|#var_name: &mut #inner_type| { - #wrapped - }) - }; - } - wrapped - } else { - quote! { #(#fn_body_stmts)* } - }; - - // Build the Renderer::render body with resource injection - // The user's body now directly creates and returns a RenderResult. - let render_fn_body = if has_resources { - quote! { - #(#immut_resource_stmts)* - #inner_body_with_resources - } - } else { - quote! { #inner_body_with_resources } - }; - - // 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_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)* - #[doc(hidden)] - #[allow(non_camel_case_types)] - #vis struct #struct_name; - - ::mingling::macros::register_renderer!(#previous_type, #struct_name); - - impl ::mingling::Renderer for #struct_name { - type Previous = #previous_type; - - fn render(#prev_param: Self::Previous) -> ::mingling::RenderResult { - #render_fn_body - } - } - - // Keep the original function unchanged - #(#fn_attrs)* - #vis fn #fn_name(#original_inputs) -> #original_return_type { - #(#fn_body_stmts)* - } - }; - - expanded.into() -} - -/// Builds the renderer entry for the global renderers list -pub fn build_renderer_entry( - struct_name: &syn::Ident, - previous_type: &TypePath, -) -> proc_macro2::TokenStream { - let enum_variant = &previous_type.path.segments.last().unwrap().ident; - quote! { - #struct_name => #enum_variant, - } -} - -/// Builds the renderer existence check entry -pub fn build_renderer_exist_entry(previous_type: &TypePath) -> proc_macro2::TokenStream { - let enum_variant = &previous_type.path.segments.last().unwrap().ident; - quote! { - Self::#enum_variant => true, - } -} - -/// Builds the structural renderer entry -#[cfg(feature = "structural_renderer")] -pub fn build_structural_renderer_entry(previous_type: &TypePath) -> proc_macro2::TokenStream { - let enum_variant = &previous_type.path.segments.last().unwrap().ident; - quote! { - Self::#enum_variant => { - // SAFETY: Only types that match will enter this branch for forced conversion, - // and `AnyOutput::new` ensures the type implements serde::Serialize - let raw = unsafe { any.restore::<#previous_type>().unwrap_unchecked() }; - let mut __renderer_inner_result = ::mingling::RenderResult::default(); - ::mingling::StructuralRenderer::render(&raw, setting, &mut __renderer_inner_result)?; - Ok(__renderer_inner_result) - } - } -} - -pub fn register_renderer(input: TokenStream) -> TokenStream { - // Parse the input as a comma-separated list of arguments - let input_parsed = syn::parse_macro_input!(input with syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>::parse_terminated); - - // Check that there are exactly two elements - if input_parsed.len() != 2 { - return syn::Error::new( - input_parsed.span(), - "Expected exactly two comma-separated arguments: `PreviousType, StructName`", - ) - .to_compile_error() - .into(); - } - - // Extract the two elements - let previous_type_expr = &input_parsed[0]; - let struct_name_expr = &input_parsed[1]; - - // Convert expressions to TypePath and Ident - let previous_type = match syn::parse2::<TypePath>(previous_type_expr.to_token_stream()) { - Ok(ty) => ty, - Err(e) => return e.to_compile_error().into(), - }; - - let struct_name = match syn::parse2::<syn::Ident>(struct_name_expr.to_token_stream()) { - Ok(ident) => ident, - Err(e) => return e.to_compile_error().into(), - }; - - // Register the renderer in the global list - let renderer_entry = build_renderer_entry(&struct_name, &previous_type); - let renderer_exist_entry = build_renderer_exist_entry(&previous_type); - #[cfg(feature = "structural_renderer")] - let structural_renderer_entry = build_structural_renderer_entry(&previous_type); - - let renderer_entry_str = renderer_entry.to_string(); - let renderer_exist_entry_str = renderer_exist_entry.to_string(); - - #[cfg(feature = "structural_renderer")] - let structural_renderer_entry_str = structural_renderer_entry.to_string(); - - // Check for duplicate variant before acquiring other locks - let variant_name = previous_type - .path - .segments - .last() - .unwrap() - .ident - .to_string(); - { - let renderers = get_global_set(&crate::RENDERERS).lock().unwrap(); - if let Err(err) = crate::check_duplicate_variant( - &renderers, - &renderer_entry_str, - &variant_name, - "renderer", - previous_type.span(), - ) { - return err.into(); - } - } // renderers lock released here - - let mut renderers = get_global_set(&crate::RENDERERS).lock().unwrap(); - let mut renderer_exist = get_global_set(&crate::RENDERERS_EXIST).lock().unwrap(); - - #[cfg(feature = "structural_renderer")] - let mut structural_renderers = get_global_set(&crate::STRUCTURAL_RENDERERS).lock().unwrap(); - - renderers.insert(renderer_entry_str); - renderer_exist.insert(renderer_exist_entry_str); - - // Only register structural renderer if the type is in STRUCTURED_TYPES - #[cfg(feature = "structural_renderer")] - { - let is_structured = get_global_set(&crate::STRUCTURED_TYPES) - .lock() - .unwrap() - .contains(&variant_name); - if is_structured { - structural_renderers.insert(structural_renderer_entry_str); - } - } - - quote! {}.into() -} |
