From b9edeb9848e4a423e133fa2a13dede6d128d6f08 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Tue, 14 Apr 2026 22:47:08 +0800 Subject: Add documentation for Mingling CLI framework --- docs/pages/1-get-started.md | 69 +++++++++++++++ docs/pages/2-basic.md | 16 ++++ docs/pages/2-basic/1-program.md | 117 +++++++++++++++++++++++++ docs/pages/2-basic/2-setup.md | 169 +++++++++++++++++++++++++++++++++++++ docs/pages/2-basic/3-dispatcher.md | 12 +++ docs/pages/2-basic/4-chain.md | 10 +++ docs/pages/2-basic/5-renderer.md | 6 ++ 7 files changed, 399 insertions(+) create mode 100644 docs/pages/1-get-started.md create mode 100644 docs/pages/2-basic.md create mode 100644 docs/pages/2-basic/1-program.md create mode 100644 docs/pages/2-basic/2-setup.md create mode 100644 docs/pages/2-basic/3-dispatcher.md create mode 100644 docs/pages/2-basic/4-chain.md create mode 100644 docs/pages/2-basic/5-renderer.md (limited to 'docs/pages') diff --git a/docs/pages/1-get-started.md b/docs/pages/1-get-started.md new file mode 100644 index 0000000..f4edfc0 --- /dev/null +++ b/docs/pages/1-get-started.md @@ -0,0 +1,69 @@ +# Get Started +This article explains how to quickly create your first **Mingling** command-line program. + +## Quick Start +1. Add `mingling` to your Rust project. +```bash +cargo add mingling +``` +Or add the following to your `Cargo.toml`: +```toml +[dependencies] +mingling = "0.1.5" +``` + +> **Mingling** is an **async program**, so please use `async-std`, `tokio`, or another async runtime. + +2. This article assumes you are using the `tokio` async runtime. Add the following to your `Cargo.toml`: +```toml +tokio = { + version = "1", + features = [ + "macros", + "rt", + "rt-multi-thread" + ] +} +``` + +3. Write the basic code in your `main.rs` or other program entry point. +```rust +use mingling::macros::{dispatcher, gen_program, r_println, renderer}; + +#[tokio::main] +async fn main() { + // Create ThisProgram + let mut program = ThisProgram::new(); + + // Import the dispatcher `HelloCommand` + program.with_dispatcher(HelloCommand); + + // Run the program + program.exec().await; +} + +// Define the dispatcher `HelloCommand`, which routes the "hello" subcommand to `HelloEntry` +dispatcher!("hello", HelloCommand => HelloEntry); + +// Define the renderer, which receives `HelloEntry` and renders the content +#[renderer] +fn render_hello(_prev: HelloEntry) { + r_println!("Hello, World!") +} + +// Create ThisProgram at the end of the code +gen_program!(); +``` + +4. Install your command-line program and run it. +```bash +cargo install --path ./ +your_bin hello +``` +Result: +```bash +Hello, World! +``` + +## 💡 Next Steps +> **Mingling**'s basic components [Go](./pages/2-basic) diff --git a/docs/pages/2-basic.md b/docs/pages/2-basic.md new file mode 100644 index 0000000..78d2fda --- /dev/null +++ b/docs/pages/2-basic.md @@ -0,0 +1,16 @@ +

Mingling's Basic Components

+

+ Table of Contents +

+ +--- + +Mingling abstracts the lifecycle of a CLI program into the following types: + +| Component | Description | +| :--- | :--- | +| [Program](./pages/2-basic/1-program) | Records resources for the current context | +| [Setup](./pages/2-basic/2-setup) | Bundles commonly used functionalities | +| [Dispatcher](./pages/2-basic/3-dispatcher) | Dispatches user input to specific types | +| [Chain](./pages/2-basic/4-chain) | Handles type conversion | +| [Renderer](./pages/2-basic/5-renderer) | Handles type rendering | diff --git a/docs/pages/2-basic/1-program.md b/docs/pages/2-basic/1-program.md new file mode 100644 index 0000000..fd8e986 --- /dev/null +++ b/docs/pages/2-basic/1-program.md @@ -0,0 +1,117 @@ +

Program

+

+ Mingling's Basic Components +

+ +--- + +## Intro + +`Program` is the data structure that holds the state for **Mingling** CLI programs. It manages the user's context and enables type-based dispatch. + +`Program` needs to implement the `ProgramCollect` trait, + +> but, you don't have to do this manually — + +The `mingling_macros` crate provides the `gen_program!()` macro, which can auto collect resources defined by the `dispatcher!`, `chain!`, and the `completion!` macro of the `comp` feature. + +```rust +// Define Dispatcher +dispatcher!("hello", HelloCommand => HelloEntry); + +// Define Renderer +#[renderer] +fn render_hello(_prev: HelloEntry) { + r_println!("Hello, World!") +} + +// Collect all resources here and generate ThisProgram +gen_program!(); + +// You can also explicitly declare a Program +// with a different name like this: +// gen_program!(MyProgram); +``` + +## Adding Setup + +You can use the `with_setup` function to add preset [Setup](pages/2-basic/2-setup) to your program, which provide reusable functionality. + +For example, you can use the following code to add parsing for global flags like `--confirm` / `--help` / `--quiet` to your program: + +```rust +use mingling::{ + macros::gen_program, + setup::BasicProgramSetup +}; + +#[tokio::main] +async fn main() { + let mut program = ThisProgram::new(); + // Add `BasicProgramSetup` + program.with_setup(BasicProgramSetup); + program.exec().await; +} + +// Generate `ThisProgram` +gen_program!(); +``` + +## Adding Dispatcher + +You can use `with_dispatcher` or `with_dispatchers` to add [Dispatchers](pages/2-basic/3-dispatcher) to your program to make it work: + +```rust +// Define two Dispatchers using `dispatcher!` +dispatcher!("member.add", + AddMemberCommand => AddMemberEntry); +dispatcher!("member.rm", + RemoveMemberCommand => RemoveMemberEntry); + +#[tokio::main] +async fn main() { + let mut program = ThisProgram::new(); + + // Register Dispatchers + program.with_dispatcher(AddMemberCommand); + program.with_dispatcher(RemoveMemberCommand); + + // Or use `with_dispatchers` + program.with_dispatchers(( + AddMemberCommand, + RemoveMemberCommand + )); + + program.exec().await; +} +``` + +## Parsing Global Args + +You can extract global arguments before the program runs to control the global state of the `Program`: + +```rust +#[tokio::main] +async fn main() { + let mut program = ThisProgram::new(); + + let mut output = current_dir().unwrap(); + + // Pick the "--quiet" or "-q" flag + program.global_flag(["--quiet", "-q"], |p| { + // Disable render output + p.stdout_setting.render_output = false; + }); + + // Pick the "--output" or "-O" flag, write to output + program.global_argument( + ["--output", "-O"], + |_, v| output = PathBuf::from(v) + ); + + program.exec().await; +} +``` + +## 💡 Next Page +> **Basic Component** - Setup [Go](./pages/2-basic/2-setup) diff --git a/docs/pages/2-basic/2-setup.md b/docs/pages/2-basic/2-setup.md new file mode 100644 index 0000000..8dbdd76 --- /dev/null +++ b/docs/pages/2-basic/2-setup.md @@ -0,0 +1,169 @@ +

Setup

+

+ Mingling's Basic Components +

+ +--- + +## Usage + +`Setup` is used to organize and package the initialization process of a `Program`, making the project easier to manage. It is defined as follows: + +```rust +struct MySetup; +impl ProgramSetup + for MySetup +{ + fn setup( + &mut self, + program: &mut Program + ) { + // Your setup logic + } +} +``` + +For example: + +```rust +use std::{env::current_dir, path::PathBuf}; + +use mingling::{ + Program, + macros::{dispatcher, gen_program, renderer}, + setup::ProgramSetup, +}; + +// Global state +static OUTPUT_PATH: std::sync::OnceLock + = std::sync::OnceLock::new(); + +#[tokio::main] +async fn main() { + let mut program = ThisProgram::new(); + program.with_setup(MySetup); + program.exec().await; +} + +// Define two Dispatchers using `dispatcher!` +dispatcher!("member.add", + AddMemberCommand => AddMemberEntry); +dispatcher!("member.rm", + RemoveMemberCommand => RemoveMemberEntry); + +struct MySetup; +impl ProgramSetup for MySetup { + fn setup( + &mut self, program: &mut Program + ) { + // Register Dispatchers + program.with_dispatcher(AddMemberCommand); + program.with_dispatcher(RemoveMemberCommand); + + // Initialize global output once + OUTPUT_PATH.get_or_init(|| current_dir().unwrap()); + + // Pick the "--quiet" or "-q" flag + program.global_flag(["--quiet", "-q"], |p| { + // Disable render output + p.stdout_setting.render_output = false; + }); + + // Pick the "--output" or "-O" flag, write to output + program.global_argument(["--output", "-O"], |_, v| { + let _ = OUTPUT_PATH.set(PathBuf::from(v)); + }); + } +} + +// Define empty renderer types to give the two types type IDs + +#[renderer] +fn phantom_renderer_add_member( + _prev: AddMemberEntry +) {} + +#[renderer] +fn phantom_renderer_remove_member( + _prev: RemoveMemberEntry +) {} + +gen_program!(); +``` + +## Simplified Syntax + +If you find the above declaration method too **verbose**, you can use the `program_setup!` macro to simplify it. The format is: + +```rust +#[program_setup] +fn my_setup( + program: &mut Program +) { + // Your setup logic +} +``` + +For example: + +```rust +use std::{env::current_dir, path::PathBuf}; + +use mingling::{ + Program, + macros::{ + dispatcher, + gen_program, + program_setup, + renderer + }, +}; + +static OUTPUT_PATH: std::sync::OnceLock + = std::sync::OnceLock::new(); + +#[tokio::main] +async fn main() { + let mut program = ThisProgram::new(); + program.with_setup(MySetup); + program.exec().await; +} + +dispatcher!("member.add", + AddMemberCommand => AddMemberEntry); +dispatcher!("member.rm", + RemoveMemberCommand => RemoveMemberEntry); + +#[program_setup] +fn my_setup( + program: &mut Program +) { + program.with_dispatcher(AddMemberCommand); + program.with_dispatcher(RemoveMemberCommand); + + OUTPUT_PATH.get_or_init(|| current_dir().unwrap()); + + program.global_flag(["--quiet", "-q"], |p| { + p.stdout_setting.render_output = false; + }); + + program.global_argument(["--output", "-O"], |_, v| { + let _ = OUTPUT_PATH.set(PathBuf::from(v)); + }); +} + +#[renderer] +fn phantom_renderer_add_member( + _prev: AddMemberEntry +) {} + +#[renderer] +fn phantom_renderer_remove_member( + _prev: RemoveMemberEntry +) {} + +gen_program!(); +``` + +## 💡 Next Page +> **Basic Component** - Dispatcher [Go](./pages/2-basic/3-dispatcher) diff --git a/docs/pages/2-basic/3-dispatcher.md b/docs/pages/2-basic/3-dispatcher.md new file mode 100644 index 0000000..643a752 --- /dev/null +++ b/docs/pages/2-basic/3-dispatcher.md @@ -0,0 +1,12 @@ +

Dispatcher

+

+ Mingling's Basic Components +

+ +--- + + + + +## 💡 Next Page +> **Basic Component** - Chain [Go](./pages/2-basic/4-chain) diff --git a/docs/pages/2-basic/4-chain.md b/docs/pages/2-basic/4-chain.md new file mode 100644 index 0000000..e9676c2 --- /dev/null +++ b/docs/pages/2-basic/4-chain.md @@ -0,0 +1,10 @@ +

Chain

+

+ Mingling's Basic Components +

+ +--- + + +## 💡 Next Page +> **Basic Component** - Renderer [Go](./pages/2-basic/5-renderer) diff --git a/docs/pages/2-basic/5-renderer.md b/docs/pages/2-basic/5-renderer.md new file mode 100644 index 0000000..f3e09de --- /dev/null +++ b/docs/pages/2-basic/5-renderer.md @@ -0,0 +1,6 @@ +

Renderer

+

+ Mingling's Basic Components +

+ +--- -- cgit