From 75f022be00bd1a3e0e43849888a4e06aebabc851 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sat, 16 May 2026 16:33:36 +0800 Subject: Document resource injection support in chain macro --- mingling_macros/src/lib.rs | 109 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 105 insertions(+), 4 deletions(-) diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index 51c5b63..213c397 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -324,7 +324,7 @@ pub fn r_println(input: TokenStream) -> TokenStream { /// #[chain] /// fn my_step(prev: InputType) -> NextProcess { /// // transform `prev`... -/// OutputType::new(result).to_render() +/// OutputType::new(result) /// } /// /// // Explicit program name: @@ -334,6 +334,72 @@ pub fn r_println(input: TokenStream) -> TokenStream { /// } /// ``` /// +/// # Resource Injection +/// +/// The `#[chain]` macro supports automatic injection of global resources +/// via the 2nd to Nth parameters. You can read resources immutably with +/// `&T` or mutate them with `&mut T`. +/// +/// ## Immutable Resource (`&T`) +/// +/// When you write `&SomeResource` as a parameter, the macro automatically +/// resolves it from the global resource store: +/// +/// ```rust,ignore +/// #[chain] +/// fn process(prev: HelloEntry, age: &Age, name: &Name) -> NextProcess { +/// // `age` and `name` are automatically injected +/// println!("Age: {}, Name: {}", age, name); +/// NextStep::default() +/// } +/// ``` +/// +/// This expands to: +/// +/// ```rust,ignore +/// let __age_binding = ::mingling::this::().res_or_default::(); +/// let age: &Age = __age_binding.as_ref(); +/// let __name_binding = ::mingling::this::().res_or_default::(); +/// let name: &Name = __name_binding.as_ref(); +/// ``` +/// +/// ## Mutable Resource (`&mut T`) +/// +/// When you write `&mut SomeResource` as a parameter, the macro wraps the +/// function body in nested `__modify_res_and_return_any` calls: +/// +/// ```rust,ignore +/// #[chain] +/// fn process(prev: HelloEntry, count: &mut InvocationCount, name: &Name) -> NextProcess { +/// count.0 += 1; +/// println!("Invocation #{} for {}", count.0, name); +/// NextStep::default() +/// } +/// ``` +/// +/// This expands to: +/// +/// ```rust,ignore +/// let __name_binding = ::mingling::this::().res_or_default::(); +/// let name: &Name = __name_binding.as_ref(); +/// +/// ::mingling::this::().__modify_res_and_return_any(|count: &mut InvocationCount| { +/// count.0 += 1; +/// println!("Invocation #{} for {}", count.0, name); +/// NextStep::default() +/// }).into() +/// ``` +/// +/// Multiple `&mut` parameters are supported with proper nesting. +/// +/// ## Restrictions +/// +/// - The first parameter (previous type) must be taken **by move**, not by reference. +/// - Resource injection parameters **must** be references (`&T` or `&mut T`), +/// owned values are not allowed. +/// - When the `async` feature is enabled, `&mut T` cannot be used in async +/// chain functions (only `&T` is supported for async). +/// /// # Sync Example /// /// ```rust,ignore @@ -344,7 +410,26 @@ pub fn r_println(input: TokenStream) -> TokenStream { /// #[chain] /// fn greet(prev: HelloEntry) -> NextProcess { /// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); -/// MyOutput::new(name).to_render() +/// MyOutput::new(name) +/// } +/// ``` +/// +/// # Sync Example with Resource Injection +/// +/// ```rust,ignore +/// use mingling::macros::{chain, pack, gen_program, r_println}; +/// +/// #[derive(Default, Clone)] +/// struct UserName(String); +/// +/// pack!(Greeting = String); +/// pack!(DisplayCount = ()); +/// +/// #[chain] +/// fn greet(prev: HelloEntry, user_name: &UserName, count: &mut u64) -> NextProcess { +/// r_println!("User: {:?}", user_name); +/// *count += 1; +/// Greeting::new(format!("Hello, {}!", user_name.0)) /// } /// ``` /// @@ -359,13 +444,29 @@ pub fn r_println(input: TokenStream) -> TokenStream { /// async fn greet(prev: HelloEntry) -> NextProcess { /// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); /// some_async_fn(&name).await; -/// MyOutput::new(name).to_render() +/// MyOutput::new(name) +/// } +/// ``` +/// +/// # Async Example with Immutable Resource Injection +/// +/// ```rust,ignore +/// use mingling::macros::{chain, pack, gen_program}; +/// +/// pack!(MyOutput = String); +/// +/// #[chain] +/// async fn greet(prev: HelloEntry, prefix: &Prefix) -> NextProcess { +/// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); +/// some_async_fn(&name).await; +/// MyOutput::new(format!("{}{}", prefix.0, name)) /// } /// ``` /// /// # Requirements /// -/// - The function must have exactly **one** parameter (the previous type in the chain). +/// - The function must have at least **one** parameter (the previous type in the chain). +/// - The first parameter must be taken **by move**. /// - The function must return `NextProcess` (the type alias generated by `gen_program!`, which equals `ChainProcess`). /// - With the `async` feature, async functions are supported; without it, async functions are rejected. #[proc_macro_attribute] -- cgit