aboutsummaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
Diffstat (limited to 'README.md')
-rw-r--r--README.md135
1 files changed, 88 insertions, 47 deletions
diff --git a/README.md b/README.md
index 47e2e0f..50c66c6 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,7 @@
<img src="https://img.shields.io/crates/size/mingling">
<img src="https://img.shields.io/crates/v/mingling?style=flat">
<img src="https://img.shields.io/docsrs/mingling?style=flat">
+ <img src="https://img.shields.io/github/actions/workflow/status/mingling-rs/mingling/ci.yml">
</p>
> [!WARNING]
@@ -53,9 +54,8 @@
- 📖 [Mainpage](https://mingling-rs.github.io/mingling/)
- 📖 [Examples](https://mingling-rs.github.io/mingling/docs/examples.html)
- 📖 [docs.rs](https://docs.rs/mingling/latest/mingling/)
-- 📖 Helpdoc [EN](https://mingling-rs.github.io/mingling/docs/doc.html#/) [中文](https://mingling-rs.github.io/mingling/docs/_zh_CN/index.html#/)
-
-
+- 📖 [Helpdoc](https://mingling-rs.github.io/mingling/docs/doc.html#/) Or [帮助文档](https://mingling-rs.github.io/mingling/docs/_zh_CN/index.html#/)
+- 🔍 [Devdoc](https://mingling-rs.github.io/mingling/docs/dev/)
<h1 align="center">
Getting Started
@@ -65,7 +65,7 @@ Add Mingling to your `Cargo.toml`:
```toml
[dependencies.mingling]
-version = "0.2.0"
+version = "0.3.0"
features = []
```
@@ -102,11 +102,11 @@ User Input → [Dispatcher] → Entry → [Chain(s)] → Result → [Renderer]
**Step4: Render** — A **Renderer** takes that result and writes it to the terminal.
> [!NOTE]
-> A Chain can produce a **State** type to be passed to the next Chain for further processing,
+> A Chain can produce a **State** type to be passed to the next Chain for further processing,
>
-> or it can produce a **Result** type to be handed off to a Renderer.
+> or it can produce a **Result** type to be handed off to a Renderer.
-Everything in this pipeline is a **plain Rust function** with an attribute macro on top.
+Everything in this pipeline is a **plain Rust function** with an attribute macro on top.
You never need to manually implement traits or construct boilerplate.
@@ -181,27 +181,36 @@ Key points:
### 3. The Renderer — "#[renderer]" — How Output Works
-The `#[renderer]` attribute turns a function into an output handler. It receives the final result of a chain and writes it to the terminal.
+The `#[renderer]` attribute turns a function into an output handler. It receives the final result of a chain and returns a `RenderResult`.
```rust
+use mingling::macros::pack;
+use mingling::prelude::*;
+use std::io::Write;
+
pack!(ResultGreeting = String);
#[renderer]
-fn render_greeting(greeting: ResultGreeting) {
- r_println!("Hello, {}!", *greeting);
+fn render_greeting(greeting: ResultGreeting) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "Hello, {}!", *greeting).ok();
+ result
}
```
-Inside a renderer, use `r_print!` / `r_println!` to write to the output buffer. This is not `println!` — it writes into Mingling's internal `RenderResult` buffer, which is flushed at the end of the pipeline.
+Inside a renderer, create a `RenderResult`, write to it using `write!` / `writeln!` (from [`std::io::Write`](https://doc.rust-lang.org/std/io/trait.Write.html)), and return it. The output is captured in the buffer and flushed by the framework at the end of the pipeline.
You can write renderers for **any type** in your program, including error types:
```rust
use mingling::prelude::*;
+use std::io::Write;
#[renderer]
-fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) {
- r_println!("Command not found: [{}]", err.join(" "));
+fn render_dispatcher_not_found(err: ErrorDispatcherNotFound) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "Command not found: [{}]", err.join(" ")).ok();
+ result
}
```
@@ -283,14 +292,17 @@ Help is just another attribute macro. When the user passes `--help` or `-h`, the
Enable it by adding `BasicProgramSetup`:
```rust
-use mingling::{macros::help, setup::BasicProgramSetup};
+use mingling::{macros::help, prelude::*, setup::BasicProgramSetup};
+use std::io::Write;
dispatcher!("greet", CMDGreet => EntryGreet);
#[help]
-fn help_greet(_prev: EntryGreet) {
- r_println!("Usage: greet <NAME>");
- r_println!("Greets the user with the given name.");
+fn help_greet(_prev: EntryGreet) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "Usage: greet <NAME>").ok();
+ writeln!(result, "Greets the user with the given name.").ok();
+ result
}
fn main() {
@@ -362,11 +374,9 @@ fn main() {
In your `build.rs`, generate the shell scripts:
```rust
+// BUILD TIME
// Features: ["comp", "builds"]
-
-fn main() {
- mingling::build::build_comp_scripts(env!("CARGO_PKG_NAME")).unwrap();
-}
+mingling::build::build_comp_scripts(env!("CARGO_PKG_NAME")).unwrap();
```
For enum-based completions, use `suggest_enum!`:
@@ -400,6 +410,10 @@ fn complete_lang(_: &ShellContext) -> Suggest {
Mingling doesn't use `?` operator propagation. Instead, errors are just **alternative results** that flow through the same chain/render pipeline. Create error types with `pack!` and route to them with `.to_render()`:
```rust
+use mingling::macros::pack;
+use mingling::prelude::*;
+use std::io::Write;
+
dispatcher!("hello", CMDHello => EntryHello);
pack!(ResultName = String);
pack!(ErrorNoNameProvided = ());
@@ -419,13 +433,17 @@ fn handle(args: EntryHello) -> Next {
}
#[renderer]
-fn render_no_name(_: ErrorNoNameProvided) {
- r_println!("No name provided");
+fn render_no_name(_: ErrorNoNameProvided) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "No name provided").ok();
+ result
}
#[renderer]
-fn render_too_long(len: ErrorNameTooLong) {
- r_println!("Name too long: {} > 10", *len);
+fn render_too_long(len: ErrorNameTooLong) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "Name too long: {} > 10", *len).ok();
+ result
}
```
@@ -482,6 +500,8 @@ fn change_dir(prev: EntryCd, current_dir: &mut ResCurrentDir) -> Next {
Resources can also be injected into `#[renderer]`:
```rust
+use mingling::prelude::*;
+use std::io::Write;
dispatcher!("current", CMDCurrent => EntryCurrent);
@@ -491,8 +511,10 @@ struct ResCurrentDir {
}
#[renderer]
-fn render_current(_: EntryCurrent, current_dir: &ResCurrentDir) {
- r_println!("Current directory: {}", current_dir.current_dir.display());
+fn render_current(_: EntryCurrent, current_dir: &ResCurrentDir) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "Current directory: {}", current_dir.current_dir.display()).ok();
+ result
}
```
@@ -538,7 +560,8 @@ If you prefer clap's powerful argument parsing, use `#[dispatcher_clap]`. It gen
// clap = "4"
use mingling::macros::dispatcher_clap;
-use mingling::Groupped;
+use mingling::prelude::*;
+use std::io::Write;
#[derive(Default, clap::Parser, Groupped)]
#[dispatcher_clap(
@@ -555,15 +578,19 @@ pub struct EntryGreet {
}
#[renderer]
-fn render_greet(greet: EntryGreet) {
- r_print!("Hello, ");
- for _ in 0..greet.repeat { r_print!("{}", greet.name); }
- r_println!("!");
+fn render_greet(greet: EntryGreet) -> RenderResult {
+ let mut result = RenderResult::new();
+ write!(result, "Hello, ").ok();
+ for _ in 0..greet.repeat { write!(result, "{}", greet.name).ok(); }
+ writeln!(result, "!").ok();
+ result
}
#[renderer]
-fn render_parse_error(err: ErrorGreetParsed) {
- r_println!("{}", *err);
+fn render_parse_error(err: ErrorGreetParsed) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "{}", *err).ok();
+ result
}
```
@@ -689,6 +716,7 @@ use mingling::{prelude::*, setup::StructuralRendererSetup};
use mingling::Groupped;
use mingling::StructuralData;
use serde::Serialize;
+use std::io::Write;
dispatcher!("render", CMDRender => EntryRender);
@@ -705,8 +733,10 @@ fn render_info(args: EntryRender) -> Next {
}
#[renderer]
-fn render_info_result(info: ResultInfo) {
- r_println!("{} is {} years old", info.name, info.age);
+fn render_info_result(info: ResultInfo) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "{} is {} years old", info.name, info.age).ok();
+ result
}
fn main() {
@@ -742,6 +772,7 @@ Enable the `async` feature to use `async fn` inside `#[chain]`:
// Dependencies:
// tokio = { version = "1", features = ["full"] }
+use std::io::Write;
use std::time::Duration;
dispatcher!("download", CMDDownload => EntryDownload);
@@ -759,8 +790,10 @@ async fn download_file(name: String) -> ResultDownloaded {
}
#[renderer]
-fn render_downloaded(result: ResultDownloaded) {
- r_println!("\"{}\" downloaded.", *result);
+fn render_downloaded(result: ResultDownloaded) -> RenderResult {
+ let mut r = RenderResult::new();
+ writeln!(r, "\"{}\" downloaded.", *result).ok();
+ r
}
```
@@ -789,6 +822,10 @@ It must be placed **after** all your `dispatcher!`, `pack!`, `#[chain]`, `#[rend
Here's a complete, runnable program:
```rust
+use mingling::macros::pack;
+use mingling::prelude::*;
+use std::io::Write;
+
dispatcher!("greet", CMDGreet => EntryGreet);
fn main() {
@@ -810,8 +847,10 @@ fn handle_greet(args: EntryGreet) -> Next {
}
#[renderer]
-fn render_greeting(greeting: ResultGreeting) {
- r_println!("Hello, {}!", *greeting);
+fn render_greeting(greeting: ResultGreeting) -> RenderResult {
+ let mut result = RenderResult::new();
+ writeln!(result, "Hello, {}!", *greeting).ok();
+ result
}
gen_program!();
@@ -829,7 +868,7 @@ Hello, Alice!
🗺️ Roadmap 🗺️
</h1>
-- [ ] Milestone.1 "MVP"
+- [x] Milestone.1 "MVP" 🎉
- [x] [[0.1.4](https://docs.rs/mingling/0.1.4/mingling/)] [`core`] [`structural_renderer`] **Mingling** can render data into serializable formats via `--json` and `--yaml` flags
- [x] [[0.1.5](https://docs.rs/mingling/0.1.5/mingling/)] [`core`] [`comp`] **Mingling** can dynamically invoke itself to provide completions for shells like `bash`, `zsh`, `fish`, and `pwsh`
- [x] [[0.1.6](https://docs.rs/mingling/0.1.6/mingling/)] [`core`] [`comp`] **Mingling** can gather more context for smarter completions
@@ -839,13 +878,15 @@ Hello, Alice!
- [x] [[0.1.8](https://docs.rs/mingling/0.1.8/mingling/)] [`core`] [`dispatch_tree`] Converts the subcommand list into a prefix tree to improve command matching speed
- [x] [[0.1.9](https://docs.rs/mingling/0.1.9/mingling/)] [`core`] [`dev_toolkits`] Provides debugging interfaces for developers to capture invocation information when issues arise (`InvokeStackDisplay`) (indirectly implemented via `ProgramHook`)
- [x] [[0.1.9](https://docs.rs/mingling/0.1.9/mingling/)] [`core`] [`repl`] Provides REPL capability (`program.exec_repl();`)
- - [ ] [**0.2.0**] Complete documentation, tests, and examples
-
+ - [x] [[0.2.0](https://docs.rs/mingling/0.2.0/mingling/)] Complete documentation, tests, and examples
- [ ] Milestone.2 "More Comfortable Dev and User Experience"
- - [ ] [**0.2.1**] [`macros`] `r_println!` in `#[chain]` support.
- - [ ] [**0.2.5**] [`mling`] Helpdoc Maker
- - [ ] [**0.2.8**] [`picker`] A more efficient and intelligent argument parser
-
+ - [ ] [`mling` / `mingling-cli`]
+ - [ ] **Mingling** Linter
+ - [ ] **Mingling** Project Generator
+ - [ ] **Mingling** Program Installer & Manager (For development)
+ - [ ] Helpdoc Editor
+ - [ ] [`picker`] A more efficient and intelligent argument parser
+ - [x] [`macros`] Remove `r_print!` / `r_println!` macros
- [ ] Milestone.3 "Unplanned"
- [ ] ...