aboutsummaryrefslogtreecommitdiff
path: root/mingling_macros/src
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_macros/src')
-rw-r--r--mingling_macros/src/chain.rs54
1 files changed, 21 insertions, 33 deletions
diff --git a/mingling_macros/src/chain.rs b/mingling_macros/src/chain.rs
index 5044227..566e782 100644
--- a/mingling_macros/src/chain.rs
+++ b/mingling_macros/src/chain.rs
@@ -20,41 +20,16 @@ fn is_unit_return_type(sig: &Signature) -> bool {
}
}
-/// Validates that the return type is `Next`, `ChainProcess<ThisProgram>`, `()`, or omitted.
+/// Validates that the return type is acceptable.
+/// Accepts `()`, `Next`, `ChainProcess<...>`, or any type that can
+/// be converted to `ChainProcess` via `.into()` (i.e. any pack type).
fn validate_return_type(sig: &Signature) -> Result<(), proc_macro2::TokenStream> {
// `()` or omitted is always valid
if is_unit_return_type(sig) {
return Ok(());
}
- match &sig.output {
- ReturnType::Type(_, ty) => match &**ty {
- Type::Path(type_path) => {
- let last_segment = type_path.path.segments.last().unwrap();
- let ident_str = last_segment.ident.to_string();
- if ident_str == "Next" || ident_str == "ChainProcess" {
- return Ok(());
- }
- Err(syn::Error::new(
- ty.span(),
- "Chain function must return `Next`, `ChainProcess<ThisProgram>`, `()`, or omit the return type",
- )
- .to_compile_error())
- }
- _ => Err(syn::Error::new(
- ty.span(),
- "Chain function must return `Next`, `ChainProcess<ThisProgram>`, `()`, or omit the return type",
- )
- .to_compile_error()),
- },
- ReturnType::Default => {
- Err(syn::Error::new(
- sig.span(),
- "Chain function must specify a return type (must be `Next`, `ChainProcess<ThisProgram>`, or `()`)",
- )
- .to_compile_error())
- }
- }
+ Ok(())
}
/// Builds the `proc` function implementation inside the generated `Chain` impl.
@@ -71,6 +46,7 @@ fn generate_proc_fn(
fn_body_stmts: &[syn::Stmt],
is_async_fn: bool,
is_unit_return: bool,
+ origin_return_type: &proc_macro2::TokenStream,
) -> proc_macro2::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();
@@ -106,11 +82,16 @@ fn generate_proc_fn(
} else {
quote! { #wrapped_body }
};
- // Use a let-binding with explicit type annotation so that the inner
- // body's `.into()` calls resolve correctly (same as the original function).
+ // Convert the body result to `ChainProcess` using the user-declared
+ // return type as the source type for a fully-qualified `Into` call.
+ // This works for both:
+ // - `-> Next` / `-> ChainProcess`: identity `From<T> for T`
+ // - `-> PackType`: `Into<ChainProcess>` from pack!/derive
quote! {
- let __chain_result: ::mingling::ChainProcess<#program_type> = { #body };
- __chain_result
+ let __chain_result = { #body };
+ <#origin_return_type as ::std::convert::Into<
+ ::mingling::ChainProcess<#program_type>
+ >>::into(__chain_result)
}
};
@@ -234,6 +215,12 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
// Always use the default crate-defined program path
let program_type = crate::default_program_path();
+ // Extract the user's return type for the explicit Into turbofish
+ let origin_return_type = match &input_fn.sig.output {
+ ReturnType::Type(_, ty) => quote! { #ty },
+ ReturnType::Default => quote! { () },
+ };
+
// Generate the `proc` function for the Chain impl
let proc_fn = generate_proc_fn(
has_resources,
@@ -247,6 +234,7 @@ pub fn chain_attr(attr: TokenStream, item: TokenStream) -> TokenStream {
#[cfg(not(feature = "async"))]
false,
is_unit_return,
+ &origin_return_type,
);
// Preserve the original function untouched, with dead_code allowed