From bcbf0506ff255129d5a66d709bdb6aafbfbe2331 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Sat, 25 Apr 2026 19:12:00 +0800 Subject: Add async example demonstrating tokio integration --- CHANGELOG.md | 1 + Cargo.lock | 6 +++- Cargo.toml | 8 ++++- README.md | 1 + docs/README.md | 1 + docs/pages/2-basic/2-setup.md | 8 ++--- examples/example-async/Cargo.toml | 8 +++++ examples/example-async/src/main.rs | 53 +++++++++++++++++++++++++++ mingling/src/example_docs.rs | 69 ++++++++++++++++++++++++++++++++++++ mingling_macros/src/program_setup.rs | 6 ++-- 10 files changed, 152 insertions(+), 9 deletions(-) create mode 100644 examples/example-async/Cargo.toml create mode 100644 examples/example-async/src/main.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index ea00cf1..ce51fec 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ 1. Removed macro `dispatcher_render!` from `mingling_macros` 2. The `<..., Group>` in `Program` no longer requires `std::fmt::Display` +3. Changed `Program` to `Program` (merged the Group and Collect types) --- diff --git a/Cargo.lock b/Cargo.lock index 1787d82..3e81edc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -129,7 +129,11 @@ dependencies = [ [[package]] name = "example-async" -version = "0.1.0" +version = "0.0.1" +dependencies = [ + "mingling", + "tokio", +] [[package]] name = "hashbrown" diff --git a/Cargo.toml b/Cargo.toml index ff3ca49..1b69a8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,10 +4,16 @@ version = "0.1.0" edition = "2024" [workspace] -members = ["examples/example-async","mingling", "mingling_core", "mingling_macros"] +members = [ + "examples/example-async", + "mingling", + "mingling_core", + "mingling_macros", +] exclude = [ "mingling_cli", "dev_tools", + "examples/example-async", "examples/example-basic", "examples/example-completion", "examples/example-general-renderer", diff --git a/README.md b/README.md index eab4ee4..41d9f2b 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,7 @@ The Mingling project consists of two main parts: ## Example Projects - **[`examples/example-basic/`](examples/example-basic/src/main.rs)** - A simple "Hello, World!" example demonstrating the most basic usage of a Dispatcher and Renderer. +- **[`examples/example-async/`](examples/example-async/src/main.rs)** - Based on `example-basic`, demonstrates how to integrate an async runtime - **[`examples/example-picker/`](examples/example-picker/src/main.rs)** - Demonstrates how to use a Chain to process and transform command arguments. - **[`examples/example-general-renderer/`](examples/example-general-renderer/src/main.rs)** - Shows how to use a general renderer for different data types (e.g., JSON, YAML, TOML, RON). - **[`examples/example-completion/`](examples/example-completion/src/main.rs)** - An example implementing auto-completion for the shell. diff --git a/docs/README.md b/docs/README.md index 6dd9667..03e17dc 100644 --- a/docs/README.md +++ b/docs/README.md @@ -134,6 +134,7 @@ The Mingling project consists of two main parts: ## Example Projects - **[`examples/example-basic/`](examples/example-basic/src/main.rs)** - A simple "Hello, World!" example demonstrating the most basic usage of a Dispatcher and Renderer. +- **[`examples/example-async/`](examples/example-async/src/main.rs)** - Based on `example-basic`, demonstrates how to integrate an async runtime - **[`examples/example-picker/`](examples/example-picker/src/main.rs)** - Demonstrates how to use a Chain to process and transform command arguments. - **[`examples/example-general-renderer/`](examples/example-general-renderer/src/main.rs)** - Shows how to use a general renderer for different data types (e.g., JSON, YAML, TOML, RON). - **[`examples/example-completion/`](examples/example-completion/src/main.rs)** - An example implementing auto-completion for the shell. diff --git a/docs/pages/2-basic/2-setup.md b/docs/pages/2-basic/2-setup.md index 97fbc5a..102e213 100644 --- a/docs/pages/2-basic/2-setup.md +++ b/docs/pages/2-basic/2-setup.md @@ -15,12 +15,12 @@ It is defined as follows: ```rust struct MySetup; -impl ProgramSetup +impl ProgramSetup for MySetup { fn setup( &mut self, - program: &mut Program + program: &mut Program ) { // Your setup logic } @@ -89,7 +89,7 @@ If you find the above declaration method too **verbose**, you can use the `progr ```rust #[program_setup] fn my_setup( - program: &mut Program + program: &mut Program ) { // Your setup logic } @@ -127,7 +127,7 @@ dispatcher!("member.rm", #[program_setup] fn my_setup( - program: &mut Program + program: &mut Program ) { program.with_dispatcher(AddMemberCommand); program.with_dispatcher(RemoveMemberCommand); diff --git a/examples/example-async/Cargo.toml b/examples/example-async/Cargo.toml new file mode 100644 index 0000000..0089520 --- /dev/null +++ b/examples/example-async/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "example-async" +version = "0.0.1" +edition = "2024" + +[dependencies] +tokio = { version = "1", features = ["full"] } +mingling = { path = "../../mingling", features = ["async"] } diff --git a/examples/example-async/src/main.rs b/examples/example-async/src/main.rs new file mode 100644 index 0000000..29aee7e --- /dev/null +++ b/examples/example-async/src/main.rs @@ -0,0 +1,53 @@ +//! `Mingling` Example - Async +//! +//! After enabling the `async` feature: +//! 1. The `chain!` macro will support using **async** functions, +//! 2. The `exec` function of `Program` will return a `Future` for you to use with an async runtime +//! +//! ## Enable Feature +//! Enable the `async` feature for mingling in `Cargo.toml` +//! ```toml +//! [dependencies] +//! mingling = { version = "...", features = ["async"] } +//! ``` +//! +//! # How to Run +//! ```bash +//! cargo run --manifest-path ./examples/example-async/Cargo.toml -- hello World +//! ``` + +use mingling::{ + macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, + marker::NextProcess, +}; + +dispatcher!("hello", HelloCommand => HelloEntry); + +// Use Tokio async runtime +#[tokio::main] +async fn main() { + let mut program = ThisProgram::new(); + program.with_dispatcher(HelloCommand); + + // Run program + program.exec().await; +} + +pack!(Hello = String); + +// You can freely use async / non-async functions to declare your Chain + +#[chain] +// fn parse_name(prev: HelloEntry) -> NextProcess { +async fn parse_name(prev: HelloEntry) -> NextProcess { + let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); + Hello::new(name).to_render() +} + +// For renderers, you can still only use synchronous functions +#[renderer] +fn render_hello_who(prev: Hello) { + r_println!("Hello, {}!", *prev); +} + +gen_program!(); diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs index 31a298f..519c5cc 100644 --- a/mingling/src/example_docs.rs +++ b/mingling/src/example_docs.rs @@ -65,6 +65,75 @@ /// gen_program!(); /// ``` pub mod example_basic {} +/// `Mingling` Example - Async +/// +/// After enabling the `async` feature: +/// 1. The `chain!` macro will support using **async** functions, +/// 2. The `exec` function of `Program` will return a `Future` for you to use with an async runtime +/// +/// ## Enable Feature +/// Enable the `async` feature for mingling in `Cargo.toml` +/// ```toml +/// [dependencies] +/// mingling = { version = "...", features = ["async"] } +/// ``` +/// +/// # How to Run +/// ```bash +/// cargo run --manifest-path ./examples/example-async/Cargo.toml -- hello World +/// ``` +/// +/// Cargo.toml +/// ```ignore +/// [package] +/// name = "example-async" +/// version = "0.0.1" +/// edition = "2024" +/// +/// [dependencies] +/// tokio = { version = "1", features = ["full"] } +/// mingling = { path = "../../mingling", features = ["async"] } +/// ``` +/// +/// main.rs +/// ```ignore +/// use mingling::{ +/// macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, +/// marker::NextProcess, +/// }; +/// +/// dispatcher!("hello", HelloCommand => HelloEntry); +/// +/// // Use Tokio async runtime +/// #[tokio::main] +/// async fn main() { +/// let mut program = ThisProgram::new(); +/// program.with_dispatcher(HelloCommand); +/// +/// // Run program +/// program.exec().await; +/// } +/// +/// pack!(Hello = String); +/// +/// // You can freely use async / non-async functions to declare your Chain +/// +/// #[chain] +/// // fn parse_name(prev: HelloEntry) -> NextProcess { +/// async fn parse_name(prev: HelloEntry) -> NextProcess { +/// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string()); +/// Hello::new(name).to_render() +/// } +/// +/// // For renderers, you can still only use synchronous functions +/// #[renderer] +/// fn render_hello_who(prev: Hello) { +/// r_println!("Hello, {}!", *prev); +/// } +/// +/// gen_program!(); +/// ``` +pub mod example_async {} /// `Mingling` Example - Completion /// /// # How to Deploy diff --git a/mingling_macros/src/program_setup.rs b/mingling_macros/src/program_setup.rs index f68ea40..ee6f054 100644 --- a/mingling_macros/src/program_setup.rs +++ b/mingling_macros/src/program_setup.rs @@ -1,6 +1,6 @@ //! Setup Attribute Macro Implementation //! -//! This module provides the `#[setup]` attribute macro for automatically +//! This module provides the `#[program_setup]` attribute macro for automatically //! generating structs that implement the `ProgramSetup` trait from functions. use proc_macro::TokenStream; @@ -53,7 +53,7 @@ fn extract_return_type(sig: &Signature) -> syn::Result<()> { } pub fn setup_attr(attr: TokenStream, item: TokenStream) -> TokenStream { - // Parse the attribute arguments (e.g., MyProgram from #[setup(MyProgram)]) + // Parse the attribute arguments (e.g., MyProgram from #[program_setup(MyProgram)]) // If no argument is provided, use ThisProgram let (program_name, use_crate_prefix) = if attr.is_empty() { ( @@ -91,7 +91,7 @@ pub fn setup_attr(attr: TokenStream, item: TokenStream) -> TokenStream { // Get function attributes (excluding the setup attribute) let mut fn_attrs = input_fn.attrs.clone(); - // Remove any #[setup(...)] attributes to avoid infinite recursion + // Remove any #[program_setup(...)] attributes to avoid infinite recursion fn_attrs.retain(|attr| !attr.path().is_ident("setup")); // Get function visibility -- cgit