diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-04-28 22:39:59 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-04-28 22:39:59 +0800 |
| commit | 85ee549f68449bc70a7f1271a93ad26a8207ee40 (patch) | |
| tree | bfb0b678d0f96c06b196417fd612a9cad2baf7fe /docs/pages/1-get-started.md | |
| parent | 5bf4209bd138faf76e3bd316fdfa128a08f2bb2e (diff) | |
Rebuild and rewrite the documentation site infrastructure
Diffstat (limited to 'docs/pages/1-get-started.md')
| -rw-r--r-- | docs/pages/1-get-started.md | 278 |
1 files changed, 234 insertions, 44 deletions
diff --git a/docs/pages/1-get-started.md b/docs/pages/1-get-started.md index d3358f4..1627ac4 100644 --- a/docs/pages/1-get-started.md +++ b/docs/pages/1-get-started.md @@ -1,66 +1,256 @@ -# Get Started -This article explains how to quickly create your first **Mingling** command-line program. +<h1 align="center">Get Started</h1> +<p align="center"> + Welcome to Mìng Lìng +</p> + +## Intro + + This chapter will guide you through **Mingling** step by step. + + Before we start, let me explain what **Mingling** can do: + + Without extra features, it is a sub-command dispatch system based on `proc-macro`: it matches user input, finds & creates the corresponding data, then pushes that data into a dispatcher that continually transforms its type. When the data can no longer be transformed, the program renders the final result to the terminal. + + In other words, you need to understand a new dev paradigm: **a fully type-based dispatch system**. This may feel **frustrating** at first, but once you get the hang of it, you'll be able to write CLI apps that are super easy to modify and extend. + + + +## Creating a Basic Program + + Next I'll walk you through creating a basic program—I assume you already have an empty Rust project ready! + +#### 1. Add Dependencies + + Add the following deps to `Cargo.toml` ✏️ -## 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.6" +mingling = "0.1.7" + +# If you want the latest, try the version hosted on Github +mingling = { git = "https://github.com/catilgrass/mingling", branch = "main" } +``` + +> [!NOTE] +> +> This version matches the **Mingling** version used when writing this doc. Check [crates.io](https://crates.io/crates/mingling) for the latest release! 😄 +> +> **Mingling** docs are actively updated to keep pace with the latest version. + + + +#### 2. Create the Program + + Now, create the program in `src/main.rs` ✏️ + +```rust +fn main() { + // Create ThisProgram and run it + ThisProgram::new().exec(); +} + +// The gen_program! macro collects *all preceding* components & types +// then generates the `ThisProgram` struct +mingling::macros::gen_program!(); +``` + +> [!TIP] +> +> When `gen_program!()` expands, it gathers info from other components & types that were expanded before it. This means you must place `gen_program!()` at the very last expansion point in the crate. +> +> I recommend putting it at the end of `main.rs` or `lib.rs`. + + + +#### 3. Create a Command + + Of course, the program currently does nothing—it won't output anything at runtime. So let's create our first command `greet` and say hi to someone ✏️ + +```rust +fn main() { + // ... +} + +// Create a dispatcher, binding GreetCommand to the "greet" sub-command +// When the user specifies this command, send GreetEntry to the dispatcher +dispatcher!("greet", GreetCommand => GreetEntry); + +// ... +gen_program!(); ``` -2. Write the basic code in your `main.rs` or other program entry point. + Don't be scared by the sudden macro and two new types! Let me explain what this macro does: + +##### About the `dispatcher!` macro 💡 + +1. It creates a `GreetCommand` struct and implements the `Dispatcher` trait + + *This tells the framework: there's a new dispatcher that will handle a sub-command's behavior.* + +2. It implements the `Dispatcher` trait's `node(&self) -> Node` function, setting the node to `"greet"` + + *This tells the framework: this dispatcher handles the `"greet"` sub-command.* + +3. It implements the `Dispatcher` trait's `begin` function, converting the user's full input into the first type `GreetEntry` + + *This tells the framework: when this dispatcher is matched, it sends a `GreetEntry` type to the dispatcher for further processing.* + + In short: **"When user types `greet`, I create a `GreetEntry` and throw it into the dispatcher for conversion."** + + + +#### 4. Register the Command + + After creating the `Dispatcher`, we have two types: `GreetCommand` and `GreetEntry`. First, register `GreetCommand` with `ThisProgram` ✏️ + ```rust -use mingling::macros::{dispatcher, gen_program, r_println, renderer}; - fn main() { - // Create ThisProgram let mut program = ThisProgram::new(); - - // Import the dispatcher `HelloCommand` - program.with_dispatcher(HelloCommand); - - // Run the program + + // Register the dispatcher + program.with_dispatcher(GreetCommand); program.exec(); } - -// 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 +``` + + Now `ThisProgram` recognizes the `"greet"` sub-command, but the framework still doesn't know what `"greet"` should do. That's where we implement the actual logic: + + + +#### 5. Implement Rendering Behavior + + We want `"greet"` to output `"Hello, World"`: since we're outputting to the screen, we can use another **Mingling** component, `Renderer`, which handles rendering data to the terminal ✏️ + +```rust +// ... +dispatcher!("greet", GreetCommand => GreetEntry); + +// Declare a renderer `render_greet`, specifying the previous type as `GreetEntry` #[renderer] -fn render_hello(_prev: HelloEntry) { - r_println!("Hello, World!") +fn render_greet(_prev: GreetEntry) { + r_println!("Hello, World!"); } - -// Create ThisProgram at the end of the code -gen_program!(); + +// ... +gen_program!(); // The renderer will be registered with the program ``` -3. Install your command-line program and run it. -```bash -cargo install --path ./ -your_bin hello + For functions marked with `#[renderer]`, **Mingling** strictly enforces only one function signature: + +```rust +#[renderer] +fn renderer_name (_prev: PreviousType) { } ``` -Result: -```bash -Hello, World! + + The macro reads the type of the first param and tells `gen_program!` that this function renders that type. + +##### About `r_println!()` 💡 + + You might notice that the print macro used inside `#[renderer]` is `r_println!` instead of `println!`. This is because the framework's rendering logic doesn't happen inside that function: after `#[renderer]` expands, it injects a `r: &mut RenderResult` into the function; `r_println!` appends the message to the `RenderResult`, and after the dispatcher closes, the final rendered data is handed to `Program::exec` for output. + + + +#### 6. Add Execution Logic + + I bet you're already itching to implement something like `greet Alice` to output `"Hello, Alice!"`—and this section is about to do just that! + + **Mingling**'s core execution flow is `Dispatcher -> Chain -> Renderer`, and the key part is `Chain`: it converts the input data type into another type, then lets the dispatcher find the next `Chain` or `Renderer` based on the result type ✏️ + +```rust +dispatcher!("greet", GreetCommand => GreetEntry); + +// Wrap the intermediate type `ResultGreetSomeone` +pack!(ResultGreetSomeone = String); + +#[chain] +fn handle_greet_entry(prev: GreetEntry) -> NextProcess { + let args = prev.inner; + let name = args + .first() + .cloned() + .unwrap_or_else(|| "World".to_string()); + + // Wrap into intermediate type + ResultGreetSomeone::new(name) +} + +#[renderer] +fn render_greet_someone(prev: ResultGreetSomeone) { + // Deref prev to get the raw type + r_println!("Hello, {}!", *prev); +} +``` + + Just like `#[renderer]`, we created a `#[chain]` that processes type `GreetEntry` and outputs `ResultGreetSomeone`. + + This inserts a `Chain` between the original `Dispatcher` and `Renderer`: it extracts the user's input params (or falls back to "World"), then passes them to the renderer to print to the terminal. + +##### About `NextProcess` 💡 + + `NextProcess` is a placeholder generated by `gen_program!()`. After `#[chain]` expands, it's replaced by a type-erased type `ChainProcess<ThisProgram>` that the dispatcher can recognize, helping reduce boilerplate code. + +> [!NOTE] +> +> `NextProcess` is a temporary solution; the next update will wait until Rust's `Impl In Type Aliases` feature is stable. +> +> **But don't worry**: the next `NextProcess` update won't introduce **breaking changes!** + +##### About `pack!` 💡 + + `pack!` is an **extremely** frequently used macro in **Mingling** development: it wraps any type into another type and auto-derives the traits the framework needs. + + Its syntax is as simple as you see: + +```rust +pack!(PackedType = RawType); ``` -## About Async Runtime + Note: `pack!` doesn't support types with lifetimes, because types are always moved (not borrowed) between dispatchers. + + + +#### 7. Compile & Run + + Alright, we've completed a basic CLI app. Here's the full code—you can paste it and run it directly: + +```rust +use mingling::macros::{chain, dispatcher, gen_program, pack, r_println, renderer}; + +fn main() { + let mut program = ThisProgram::new(); + program.with_dispatcher(GreetCommand); + program.exec(); +} + +dispatcher!("greet", GreetCommand => GreetEntry); -**Mingling** supports **async runtime**, you can enable the `async` feature to activate it. +pack!(ResultGreetSomeone = String); -After enabling it, **Mingling** will have the following changes: +#[chain] +fn handle_greet_entry(prev: GreetEntry) -> NextProcess { + let args = prev.inner; + let name = args.first().cloned().unwrap_or_else(|| "World".to_string()); -- The `Chain` trait and `chain!` macro will require you to use **async functions** -- `Program::exec` will become an async function -- The `gen_program!` macro will generate async functions + ResultGreetSomeone::new(name) +} + +#[renderer] +fn render_greet_someone(prev: ResultGreetSomeone) { + r_println!("Hello, {}!", *prev); +} -**Mingling** does not depend on any specific asynchronous runtime internally, which means you can freely choose a suitable asynchronous runtime for your program (such as `async-std`, `tokio`) +gen_program!(); +``` + + Output: + +```bash +~> your-bin greet +Hello, World! +~> your-bin greet Alice +Hello, Alice! +``` -## 💡 Next Steps -> **Mingling**'s basic components [Go](./pages/2-basic) +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> |
