From 1f583f625a7d541dc7d47a8136b31d29a5ed5441 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Mon, 20 Jul 2026 00:59:17 +0800 Subject: chore(macros): reorganize module hierarchy in mingling_macros Restructure flat module layout into logical directories (attr/, derive/, func/, systems/, extensions/, utils.rs). Tighten internal function visibility to pub(crate). All public API signatures remain unchanged. --- mingling_macros/src/completion.rs | 239 -------------------------------------- 1 file changed, 239 deletions(-) delete mode 100644 mingling_macros/src/completion.rs (limited to 'mingling_macros/src/completion.rs') diff --git a/mingling_macros/src/completion.rs b/mingling_macros/src/completion.rs deleted file mode 100644 index ae01462..0000000 --- a/mingling_macros/src/completion.rs +++ /dev/null @@ -1,239 +0,0 @@ -use crate::res_injection::{ResourceInjection, generate_immut_resource_bindings}; -use proc_macro::TokenStream; -use quote::quote; -use syn::spanned::Spanned; -use syn::{FnArg, Ident, ItemFn, Pat, PatType, Type, TypePath, parse_macro_input}; - -#[cfg(feature = "comp")] -pub fn completion_attr(attr: TokenStream, item: TokenStream) -> TokenStream { - // Parse the attribute arguments such as HelloEntry or crate::EntryFine from #[completion(crate::EntryFine)] - use crate::get_global_set; - let previous_type_path: TypePath = if attr.is_empty() { - return syn::Error::new( - proc_macro2::Span::call_site(), - "completion attribute requires a previous type argument, e.g. #[completion(HelloEntry)]", - ) - .to_compile_error() - .into(); - } else { - parse_macro_input!(attr as TypePath) - }; - let previous_type_ident = &previous_type_path.path.segments.last().unwrap().ident; - - // 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(), "Completion function cannot be async") - .to_compile_error() - .into(); - } - - // Get the function signature parts - let sig = &input_fn.sig; - let inputs = &sig.inputs; - let output = &sig.output; - - // Must have at least one parameter ctx - if inputs.is_empty() { - return syn::Error::new( - inputs.span(), - "Completion function must have at least one parameter: `ctx: &ShellContext`", - ) - .to_compile_error() - .into(); - } - - // Extract the first param pattern and type for the ctx parameter - let first_arg = &inputs[0]; - let (ctx_pat, _ctx_type) = match first_arg { - FnArg::Typed(PatType { pat, ty, .. }) => { - let param_pat = (**pat).clone(); - (param_pat, (**ty).clone()) - } - FnArg::Receiver(_) => { - return syn::Error::new( - first_arg.span(), - "Completion function cannot have self parameter", - ) - .to_compile_error() - .into(); - } - }; - - // Extract resources from params 2 through N, skipping ctx - let resources = match extract_resources_from_args(sig, 1) { - Ok(r) => r, - Err(e) => return e.to_compile_error().into(), - }; - - // Get the function body - let fn_body = &input_fn.block; - let fn_body_stmts = &fn_body.stmts; - - // Get function attributes excluding the completion attribute - let mut fn_attrs = input_fn.attrs.clone(); - fn_attrs.retain(|attr| !attr.path().is_ident("completion")); - - // Get function visibility - let vis = &input_fn.vis; - - // Get function name - let fn_name = &sig.ident; - - // Generate internal name from function name using snake_case - let internal_name = format!( - "__internal_completion_{}", - just_fmt::snake_case!(fn_name.to_string()) - ); - let struct_name = Ident::new(&internal_name, fn_name.span()); - - let program_type = crate::default_program_path(); - let has_resources = !resources.is_empty(); - let mut_resources: Vec<_> = resources.iter().filter(|r| r.is_mut).collect(); - - // Generate immutable resource bindings - let immut_resource_stmts = generate_immut_resource_bindings(resources.iter(), &program_type); - - // Build the comp method body with resource injection - // Use modify_res for mutable resources same pattern as renderer.rs - let wrapped_body = if mut_resources.is_empty() { - quote! { #(#fn_body_stmts)* } - } else { - 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 - }; - - let comp_body = if has_resources { - quote! { - #(#immut_resource_stmts)* - #wrapped_body - } - } else { - quote! { #(#fn_body_stmts)* } - }; - - // Generate the struct and implementation - // The `comp` trait method only takes `ctx` as the first parameter; resources are injected internally - - let expanded = quote! { - #(#fn_attrs)* - #[doc(hidden)] - #[allow(non_camel_case_types)] - #vis struct #struct_name; - - impl ::mingling::Completion for #struct_name { - type Previous = #previous_type_path; - - fn comp(#ctx_pat: &::mingling::ShellContext) #output { - #comp_body - } - } - - // Keep the original function for internal use - #(#fn_attrs)* - #vis fn #fn_name(#inputs) #output { - #fn_body - } - }; - - let completion_entry = quote! { - Self::#previous_type_ident => <#struct_name as ::mingling::Completion>::comp(ctx), - }; - - let mut completions = get_global_set(&crate::COMPLETIONS).lock().unwrap(); - let completion_str = completion_entry.to_string(); - - // Check for duplicate variant before inserting - let variant_name = previous_type_ident.to_string(); - if let Err(err) = crate::check_duplicate_variant( - &completions, - &completion_str, - &variant_name, - "completion", - previous_type_path.span(), - ) { - return err.into(); - } - - completions.insert(completion_str); - - expanded.into() -} - -/// Extract resource injection parameters from function arguments (skipping the first N params). -fn extract_resources_from_args( - sig: &syn::Signature, - skip: usize, -) -> syn::Result> { - let mut resources = Vec::new(); - for arg in sig.inputs.iter().skip(skip) { - match arg { - FnArg::Typed(PatType { pat, ty, .. }) => { - let var_name = match &**pat { - Pat::Ident(pat_ident) => pat_ident.ident.clone(), - _ => { - return Err(syn::Error::new( - pat.span(), - "Resource injection parameter must be a simple identifier", - )); - } - }; - - let full_type = *(*ty).clone(); - - let (inner_type, is_ref, is_mut) = match &full_type { - Type::Reference(ref_type) => match &*ref_type.elem { - Type::Path(type_path) => { - let is_mut = ref_type.mutability.is_some(); - (type_path.clone(), true, is_mut) - } - _ => { - return Err(syn::Error::new( - ty.span(), - "Reference resource type must be a type path", - )); - } - }, - Type::Path(_) => { - return Err(syn::Error::new( - ty.span(), - "Resource injection parameter must be a reference (`&T` or `&mut T`)", - )); - } - _ => { - return Err(syn::Error::new( - ty.span(), - "Resource injection type must be a type path or reference", - )); - } - }; - - resources.push(ResourceInjection { - var_name, - full_type, - inner_type, - is_ref, - is_mut, - }); - } - FnArg::Receiver(_) => { - return Err(syn::Error::new( - arg.span(), - "Resource injection parameter cannot be self", - )); - } - } - } - Ok(resources) -} -- cgit