diff options
Diffstat (limited to 'mingling_macros/src/res_injection.rs')
| -rw-r--r-- | mingling_macros/src/res_injection.rs | 250 |
1 files changed, 0 insertions, 250 deletions
diff --git a/mingling_macros/src/res_injection.rs b/mingling_macros/src/res_injection.rs deleted file mode 100644 index 09da889..0000000 --- a/mingling_macros/src/res_injection.rs +++ /dev/null @@ -1,250 +0,0 @@ -use quote::quote; -use syn::spanned::Spanned; -use syn::{FnArg, Ident, Pat, PatType, Signature, Type, TypePath}; - -/// Extracted information about a resource injection parameter -pub(crate) struct ResourceInjection { - pub(crate) var_name: Ident, - pub(crate) full_type: Type, - pub(crate) inner_type: TypePath, - pub(crate) is_ref: bool, - pub(crate) is_mut: bool, -} - -/// Extracts the previous type and parameter name from function arguments, -/// and collects resource injection parameters from the 2nd argument onward. -#[allow(clippy::too_many_lines)] -pub(crate) fn extract_args_info( - sig: &Signature, -) -> syn::Result<(Pat, TypePath, Vec<ResourceInjection>)> { - if sig.inputs.is_empty() { - return Err(syn::Error::new( - sig.span(), - "Function must have at least one parameter", - )); - } - - // First parameter: required, the previous type (must be owned, not a reference) - let first_arg = &sig.inputs[0]; - let (prev_param, previous_type) = match first_arg { - FnArg::Typed(PatType { pat, ty, .. }) => { - let param_pat = (**pat).clone(); - match &**ty { - Type::Path(type_path) => (param_pat, type_path.clone()), - Type::Reference(_) => { - return Err(syn::Error::new( - ty.span(), - "The first parameter (previous type) must be taken by move, \ - not by reference. \ - Use `prev: SomeEntry` instead of `prev: &SomeEntry`.", - )); - } - _ => { - return Err(syn::Error::new( - ty.span(), - "First parameter type must be a type path", - )); - } - } - } - FnArg::Receiver(_) => { - return Err(syn::Error::new( - first_arg.span(), - "Function cannot have self parameter", - )); - } - }; - - // 2nd to Nth parameters: optional, for resource injection - let mut resources = Vec::new(); - for arg in sig.inputs.iter().skip(1) { - match arg { - FnArg::Typed(PatType { pat, ty, .. }) => { - // Extract the variable name – must be a simple identifier - 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 (e.g., `age: &Age`)", - )); - } - }; - - let full_type = *(*ty).clone(); - - // Try to extract inner type for reference patterns like `&Age` -> `Age` - // and `&mut Age` -> `Age` - 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 (e.g., `age: &Age`)", - )); - } - }, - Type::Path(_) => { - return Err(syn::Error::new( - ty.span(), - "Resource injection parameter must be a reference (`&T` or `&mut T`), \ - not an owned value. Use `age: &Age` instead of `age: Age`.", - )); - } - _ => { - return Err(syn::Error::new( - ty.span(), - "Resource injection type must be a type path or reference to one \ - (e.g., `age: Age` or `age: &Age`)", - )); - } - }; - - 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((prev_param, previous_type, resources)) -} - -/// Generates `let` binding statements for immutable resource injection parameters. -/// -/// Each immutable reference parameter gets a `_binding` variable that holds the -/// `res_or_default` result, then a shadowing `let` that borrows from it via `.as_ref()`. -pub(crate) fn generate_immut_resource_bindings<'a>( - resources: impl Iterator<Item = &'a ResourceInjection>, - program_type: &proc_macro2::TokenStream, -) -> Vec<proc_macro2::TokenStream> { - resources - .filter(|r| !r.is_mut) - .map(|res| { - let var_binding_name = syn::Ident::new( - &format!("__{}_binding", &res.var_name.to_string()), - res.var_name.span(), - ); - let var_name = &res.var_name; - let full_type = &res.full_type; - let inner_type = &res.inner_type; - if res.is_ref { - quote! { - let #var_binding_name = ::mingling::this::<#program_type>() - .res_or_default::<#inner_type>(); - let #var_name: #full_type = #var_binding_name.as_ref(); - } - } else { - quote! { - let #var_name: #full_type = ::mingling::this::<#program_type>() - .res_or_default::<#full_type>(); - } - } - }) - .collect() -} - -/// Generates a unique binding name for a mutable resource variable. -fn mut_res_binding_name(var_name: &Ident) -> Ident { - syn::Ident::new(&format!("__{}_binding", var_name), var_name.span()) -} - -/// Wraps the function body in nested `__modify_res_and_return_route` closures for -/// each mutable resource parameter (sync version). -/// -/// The innermost closure gets the original body, and each mutable parameter wraps -/// outward from last to first. -pub(crate) fn wrap_body_with_mut_resources( - fn_body_stmts: &[syn::Stmt], - mut_resources: &[&ResourceInjection], - program_type: &proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - let mut wrapped = quote! { - #(#fn_body_stmts)* - }; - - for res in mut_resources { - let var_name = &res.var_name; - let inner_type = &res.inner_type; - wrapped = quote! { - ::mingling::this::<#program_type>().__modify_res_and_return_route(|#var_name: &mut #inner_type| { - #wrapped - }).into() - }; - } - - wrapped -} - -/// Generates code for mutable resource injection in async `#[chain]` functions. -/// -/// Instead of wrapping in a closure (which causes lifetime issues with async), -/// this generates a three-part pattern: -/// -/// 1. **Extract** each mutable resource from the global store into an owned local. -/// 2. **Body block** — the user's body with `&mut` references scoped to a block. -/// 3. **Store back** each modified resource after the body block completes. -/// -/// This avoids holding a `&mut` reference across `.await` points — the borrow -/// ends when the body block closes. -pub(crate) fn wrap_body_with_mut_resources_async( - fn_body_stmts: &[syn::Stmt], - mut_resources: &[&ResourceInjection], - program_type: &proc_macro2::TokenStream, -) -> proc_macro2::TokenStream { - // 1. Generate extract statements and body-block `let` bindings - let mut extract_stmts = Vec::new(); - let mut body_lets = Vec::new(); - - for res in mut_resources { - let var_name = &res.var_name; - let inner_type = &res.inner_type; - let binding_name = mut_res_binding_name(var_name); - - extract_stmts.push(quote! { - let mut #binding_name = ::mingling::this::<#program_type>() - .__extract_res_mut::<#inner_type>(); - }); - - body_lets.push(quote! { - let #var_name: &mut #inner_type = &mut #binding_name; - }); - } - - // 2. Store-back statements (reverse order is fine, resources are independent) - let mut store_stmts = Vec::new(); - for res in mut_resources { - let var_name = &res.var_name; - let inner_type = &res.inner_type; - let binding_name = mut_res_binding_name(var_name); - - store_stmts.push(quote! { - ::mingling::this::<#program_type>() - .__store_res::<#inner_type>(#binding_name); - }); - } - - quote! { - #(#extract_stmts)* - let __chain_result = { - #(#body_lets)* - #(#fn_body_stmts)* - }; - #(#store_stmts)* - __chain_result - } -} |
