aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/.nojekyll0
-rw-r--r--docs/LICENSE21
-rw-r--r--docs/README.md157
-rw-r--r--docs/_sidebar.md9
-rw-r--r--docs/css/vue.css885
-rw-r--r--docs/index.html55
-rw-r--r--docs/pages/1-get-started.md69
-rw-r--r--docs/pages/2-basic.md16
-rw-r--r--docs/pages/2-basic/1-program.md117
-rw-r--r--docs/pages/2-basic/2-setup.md169
-rw-r--r--docs/pages/2-basic/3-dispatcher.md12
-rw-r--r--docs/pages/2-basic/4-chain.md10
-rw-r--r--docs/pages/2-basic/5-renderer.md6
13 files changed, 1526 insertions, 0 deletions
diff --git a/docs/.nojekyll b/docs/.nojekyll
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/docs/.nojekyll
diff --git a/docs/LICENSE b/docs/LICENSE
new file mode 100644
index 0000000..bec4d76
--- /dev/null
+++ b/docs/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2021 docsifyjs
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 0000000..15e3230
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,157 @@
+<p align="center">
+ <a href="https://github.com/CatilGrass/mingling">
+ <img alt="Mingling" src="res/icon_shadow.png" width="30%">
+ </a>
+</p>
+<h1 align="center">Mìng Lìng - 命令</h1>
+
+<p align="center">
+ The Rust CLI Framework
+</p>
+<p align="center">
+ <img src="https://img.shields.io/github/stars/CatilGrass/mingling?style=for-the-badge">
+ <a href="https://crates.io/crates/mingling">
+ <img src="https://img.shields.io/crates/v/mingling?style=for-the-badge">
+ </a>
+ <a href="https://docs.rs/mingling/0.1.5/mingling/">
+ <img src="https://img.shields.io/docsrs/mingling?style=for-the-badge">
+ </a>
+ <img src="https://img.shields.io/badge/Current-0.1.5-green?style=for-the-badge">
+</p>
+
+> [!WARNING]
+>
+> **Note**: Mingling is still under active development, and its API may change. Feel free to try it out and give us feedback!
+> **Hint**: This note will be removed in version `0.2.0`
+
+## Contents
+
+- [Intro](#intro)
+- [Quick Start](#quick-start)
+- [Core Concepts](#core-concepts)
+- [Project Structure](#project-structure)
+- [Example Projects](#example-projects)
+- [Next Steps](#next-steps)
+- [Roadmap](#roadmap)
+- [Unplanned Features](#unplanned-features)
+- [License](#license)
+
+## Intro
+
+[`Mingling`](https://github.com/CatilGrass/mingling) is a **proc-macro and type system-based** Rust CLI framework, suitable for developing complex command-line programs with numerous subcommands.
+
+> BTW: Its name comes from the Chinese Pinyin "mìng lìng", meaning "Command". 😄
+
+
+## Quick Start
+
+The example below shows how to use `Mingling` to create a simple command-line program:
+
+```rust
+use mingling::macros::{dispatcher, gen_program, r_println, renderer};
+
+#[tokio::main]
+async fn main() {
+ let mut program = ThisProgram::new();
+ program.with_dispatcher(HelloCommand);
+
+ // Execute
+ program.exec().await;
+}
+
+// Define command: "<bin> hello"
+dispatcher!("hello", HelloCommand => HelloEntry);
+
+// Render HelloEntry
+#[renderer]
+fn render_hello_world(_prev: HelloEntry) {
+ r_println!("Hello, World!")
+}
+
+// Fallbacks
+#[renderer]
+fn fallback_dispatcher_not_found(prev: DispatcherNotFound) {
+ r_println!("Dispatcher not found for command `{}`", prev.join(", "))
+}
+
+#[renderer]
+fn fallback_renderer_not_found(prev: RendererNotFound) {
+ r_println!("Renderer not found `{}`", *prev)
+}
+
+// Collect renderers and chains to generate ThisProgram
+gen_program!();
+```
+
+Output:
+
+```
+> mycmd hello
+Hello, World!
+> mycmd hallo
+Dispatcher not found for command `hallo`
+```
+
+## Core Concepts
+
+Mingling abstracts command execution into the following parts:
+
+1. **Dispatcher** - Routes user input to a specific renderer or chain based on the command node name.
+2. **Chain** - Transforms the incoming type into another type, passing it to the next chain or renderer.
+3. **Renderer** - Stops the chain and prints the currently processed type to the terminal.
+4. **Program** - Manages the lifecycle and configuration of the entire CLI application.
+
+<details>
+ <summary>Architecture Diagram (click to expand)</summary>
+ <p align="center">
+ <a href="https://github.com/CatilGrass/mingling">
+ <img alt="Mingling" src="res/graph.png" width="75%">
+ </a>
+ </p>
+</details>
+
+## Project Structure
+
+The Mingling project consists of two main parts:
+
+- **[mingling/](mingling/)** - The core runtime library, containing type definitions, error handling, and basic functionality.
+- **[mingling_macros/](mingling_macros/)** - The procedural macro library, providing declarative macros to simplify development.
+
+## 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-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.
+
+## Next Steps
+
+You can read the following docs to learn more about the `Mingling` framework:
+
+- Check out **[Mingling Examples](examples/)** to learn about the core library.
+- Check out **[mingling_macros/README.md](mingling_macros/README.md)** to learn how to use the macro system.
+
+## Roadmap
+
+- [x] core: \[[0.1.4](https://docs.rs/mingling/0.1.4/mingling/)\] General Renderers *( Json, Yaml, Toml, Ron )*
+- [x] core: \[[0.1.5](https://docs.rs/mingling/0.1.5/mingling/)\] Completion *( Bash Zsh Fish Pwsh )*
+- [ ] core: \[**0.2.0**\] Parallel Chains
+- [ ] \[**0.2.1**\] Helpdoc
+- [ ] \[**unplanned**\] Parser Theme
+- [ ] ...
+
+## Unplanned Features
+
+While Mingling has several common CLI features that are **not planned** to be directly included in the framework.
+This is because the Rust ecosystem already has excellent and mature crates to handle these issues, and Mingling's design is intended to be used in combination with them.
+
+- **Colored Output**: To add color and styles (bold, italic, etc.) to terminal output, consider using crates like [`colored`](https://crates.io/crates/colored) or [`owo-colors`](https://crates.io/crates/owo-colors). You can integrate their types directly into your renderers.
+- **I18n**: To translate your CLI application, the [`rust-i18n`](https://crates.io/crates/rust-i18n) crate provides a powerful internationalization solution that you can use in your command logic and renderers.
+- **Progress Bars**: To display progress indicators, the [`indicatif`](https://crates.io/crates/indicatif) crate is the standard choice.
+- **TUI**: To build full-screen interactive terminal applications, it is recommended to use a framework like [`ratatui`](https://crates.io/crates/ratatui) (formerly `tui-rs`).
+
+## License
+
+This project is licensed under the MIT License.
+
+See [LICENSE-MIT](LICENSE-MIT) or [LICENSE-APACHE](LICENSE-APACHE) file for details.
diff --git a/docs/_sidebar.md b/docs/_sidebar.md
new file mode 100644
index 0000000..9cf9351
--- /dev/null
+++ b/docs/_sidebar.md
@@ -0,0 +1,9 @@
+- [Welcome!](README)
+- [Get Started](./pages/1-get-started)
+
+- [Basic](./pages/2-basic)
+ - [Program](./pages/2-basic/1-program)
+ - [Setup](./pages/2-basic/2-setup)
+ - [Dispatcher](./pages/2-basic/3-dispatcher)
+ - [Chain](./pages/2-basic/4-chain)
+ - [Renderer](./pages/2-basic/5-renderer)
diff --git a/docs/css/vue.css b/docs/css/vue.css
new file mode 100644
index 0000000..499bf1c
--- /dev/null
+++ b/docs/css/vue.css
@@ -0,0 +1,885 @@
+/*
+ * Based on vue.css
+ * Original: https://cdn.jsdelivr.net/npm/docsify@4.13.1/lib/themes/vue.css
+ */
+
+@import url("https://fonts.googleapis.com/css?family=Roboto+Mono|Source+Sans+Pro:300,400,600");
+* {
+ -webkit-font-smoothing: antialiased;
+ -webkit-overflow-scrolling: touch;
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-text-size-adjust: none;
+ -webkit-touch-callout: none;
+ box-sizing: border-box;
+}
+body:not(.ready) {
+ overflow: hidden;
+}
+body:not(.ready) .app-nav,
+body:not(.ready) > nav,
+body:not(.ready) [data-cloak] {
+ display: none;
+}
+div#app {
+ font-size: 30px;
+ font-weight: lighter;
+ margin: 40vh auto;
+ text-align: center;
+}
+div#app:empty:before {
+ content: "Loading...";
+}
+img.emoji {
+ height: 1.2em;
+}
+img.emoji,
+span.emoji {
+ vertical-align: middle;
+}
+span.emoji {
+ font-family:
+ Apple Color Emoji,
+ Segoe UI Emoji,
+ Segoe UI Symbol,
+ Noto Color Emoji;
+ font-size: 1.2em;
+}
+.progress {
+ background-color: #673ab7;
+ background-color: var(--theme-color, #673ab7);
+ height: 2px;
+ left: 0;
+ position: fixed;
+ right: 0;
+ top: 0;
+ transition:
+ width 0.2s,
+ opacity 0.4s;
+ width: 0;
+ z-index: 999999;
+}
+.search .search-keyword,
+.search a:hover {
+ color: #673ab7;
+ color: var(--theme-color, #673ab7);
+}
+.search .search-keyword {
+ font-style: normal;
+ font-weight: 700;
+}
+body,
+html {
+ height: 100%;
+}
+body {
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-font-smoothing: antialiased;
+ color: #34495e;
+ font-family:
+ Source Sans Pro,
+ Helvetica Neue,
+ Arial,
+ sans-serif;
+ font-size: 15px;
+ letter-spacing: 0;
+ margin: 0;
+ overflow-x: hidden;
+}
+img {
+ max-width: 100%;
+}
+a[disabled] {
+ cursor: not-allowed;
+ opacity: 0.6;
+}
+kbd {
+ border: 1px solid #ccc;
+ border-radius: 3px;
+ display: inline-block;
+ font-size: 12px !important;
+ line-height: 12px;
+ margin-bottom: 3px;
+ padding: 3px 5px;
+ vertical-align: middle;
+}
+li input[type="checkbox"] {
+ margin: 0 0.2em 0.25em 0;
+ vertical-align: middle;
+}
+.app-nav {
+ margin: 25px 60px 0 0;
+ position: absolute;
+ right: 0;
+ text-align: right;
+ z-index: 10;
+}
+.app-nav.no-badge {
+ margin-right: 25px;
+}
+.app-nav p {
+ margin: 0;
+}
+.app-nav > a {
+ margin: 0 1rem;
+ padding: 5px 0;
+}
+.app-nav li,
+.app-nav ul {
+ display: inline-block;
+ list-style: none;
+ margin: 0;
+}
+.app-nav a {
+ color: inherit;
+ font-size: 16px;
+ text-decoration: none;
+ transition: color 0.3s;
+}
+.app-nav a.active,
+.app-nav a:hover {
+ color: #673ab7;
+ color: var(--theme-color, #673ab7);
+}
+.app-nav a.active {
+ border-bottom: 2px solid #673ab7;
+ border-bottom: 2px solid var(--theme-color, #673ab7);
+}
+.app-nav li {
+ display: inline-block;
+ margin: 0 1rem;
+ padding: 5px 0;
+ position: relative;
+ cursor: pointer;
+}
+.app-nav li ul {
+ background-color: #fff;
+ border: 1px solid;
+ border-color: #ddd #ddd #ccc;
+ border-radius: 4px;
+ box-sizing: border-box;
+ display: none;
+ max-height: calc(100vh - 61px);
+ overflow-y: auto;
+ padding: 10px 0;
+ position: absolute;
+ right: -15px;
+ text-align: left;
+ top: 100%;
+ white-space: nowrap;
+}
+.app-nav li ul li {
+ display: block;
+ font-size: 14px;
+ line-height: 1rem;
+ margin: 8px 14px;
+ white-space: nowrap;
+}
+.app-nav li ul a {
+ display: block;
+ font-size: inherit;
+ margin: 0;
+ padding: 0;
+}
+.app-nav li ul a.active {
+ border-bottom: 0;
+}
+.app-nav li:hover ul {
+ display: block;
+}
+.github-corner {
+ border-bottom: 0;
+ position: fixed;
+ right: 0;
+ text-decoration: none;
+ top: 0;
+ z-index: 1;
+}
+.github-corner:hover .octo-arm {
+ animation: octocat-wave 0.56s ease-in-out;
+}
+.github-corner svg {
+ color: #fff;
+ fill: #673ab7;
+ fill: var(--theme-color, #673ab7);
+ height: 80px;
+ width: 80px;
+}
+main {
+ display: block;
+ position: relative;
+ width: 100vw;
+ height: 100%;
+ z-index: 0;
+}
+main.hidden {
+ display: none;
+}
+.anchor {
+ display: inline-block;
+ text-decoration: none;
+ transition: all 0.3s;
+}
+.anchor span {
+ color: #34495e;
+}
+.anchor:hover {
+ text-decoration: underline;
+}
+.sidebar {
+ border-right: 1px solid rgba(0, 0, 0, 0.07);
+ overflow-y: auto;
+ padding: 40px 0 0;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ left: 0;
+ transition: transform 0.25s ease-out;
+ width: 300px;
+ z-index: 20;
+}
+.sidebar > h1 {
+ margin: 0 auto 1rem;
+ font-size: 1.5rem;
+ font-weight: 300;
+ text-align: center;
+}
+.sidebar > h1 a {
+ color: inherit;
+ text-decoration: none;
+}
+.sidebar > h1 .app-nav {
+ display: block;
+ position: static;
+}
+.sidebar .sidebar-nav {
+ line-height: 2em;
+ padding-bottom: 40px;
+}
+.sidebar li.collapse .app-sub-sidebar {
+ display: none;
+}
+.sidebar ul {
+ margin: 0 0 0 15px;
+ padding: 0;
+}
+.sidebar li > p {
+ font-weight: 700;
+ margin: 0;
+}
+.sidebar ul,
+.sidebar ul li {
+ list-style: none;
+}
+.sidebar ul li a {
+ border-bottom: none;
+ display: block;
+}
+.sidebar ul li ul {
+ padding-left: 20px;
+}
+.sidebar::-webkit-scrollbar {
+ width: 4px;
+}
+.sidebar::-webkit-scrollbar-thumb {
+ background: transparent;
+ border-radius: 4px;
+}
+.sidebar:hover::-webkit-scrollbar-thumb {
+ background: hsla(0, 0%, 53.3%, 0.4);
+}
+.sidebar:hover::-webkit-scrollbar-track {
+ background: hsla(0, 0%, 53.3%, 0.1);
+}
+.sidebar-toggle {
+ background-color: transparent;
+ background-color: hsla(0, 0%, 100%, 0.8);
+ border: 0;
+ outline: none;
+ padding: 10px;
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ text-align: center;
+ transition: opacity 0.3s;
+ width: 284px;
+ z-index: 30;
+ cursor: pointer;
+}
+.sidebar-toggle:hover .sidebar-toggle-button {
+ opacity: 0.4;
+}
+.sidebar-toggle span {
+ background-color: #673ab7;
+ background-color: var(--theme-color, #673ab7);
+ display: block;
+ margin-bottom: 4px;
+ width: 16px;
+ height: 2px;
+}
+body.sticky .sidebar,
+body.sticky .sidebar-toggle {
+ position: fixed;
+}
+.content {
+ padding-top: 60px;
+ position: absolute;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 300px;
+ transition: left 0.25s ease;
+}
+.markdown-section {
+ margin: 0 auto;
+ max-width: 80%;
+ padding: 30px 15px 40px;
+ position: relative;
+}
+.markdown-section > * {
+ box-sizing: border-box;
+ font-size: inherit;
+}
+.markdown-section > :first-child {
+ margin-top: 0 !important;
+}
+.markdown-section hr {
+ border: none;
+ border-bottom: 1px solid #eee;
+ margin: 2em 0;
+}
+.markdown-section iframe {
+ border: 1px solid #eee;
+ width: 1px;
+ min-width: 100%;
+}
+.markdown-section table {
+ border-collapse: collapse;
+ border-spacing: 0;
+ display: block;
+ margin-bottom: 1rem;
+ overflow: auto;
+ width: 100%;
+}
+.markdown-section th {
+ font-weight: 700;
+}
+.markdown-section td,
+.markdown-section th {
+ border: 1px solid #ddd;
+ padding: 6px 13px;
+}
+.markdown-section tr {
+ border-top: 1px solid #ccc;
+}
+.markdown-section p.tip,
+.markdown-section tr:nth-child(2n) {
+ background-color: #f8f8f8;
+}
+.markdown-section p.tip {
+ border-bottom-right-radius: 2px;
+ border-left: 4px solid #ff4081;
+ border-top-right-radius: 2px;
+ margin: 2em 0;
+ padding: 12px 24px 12px 30px;
+ position: relative;
+}
+.markdown-section p.tip:before {
+ background-color: #ff4081;
+ border-radius: 100%;
+ color: #fff;
+ content: "!";
+ font-family:
+ Dosis,
+ Source Sans Pro,
+ Helvetica Neue,
+ Arial,
+ sans-serif;
+ font-size: 14px;
+ font-weight: 700;
+ left: -12px;
+ line-height: 20px;
+ position: absolute;
+ height: 20px;
+ width: 20px;
+ text-align: center;
+ top: 14px;
+}
+.markdown-section p.tip code {
+ background-color: #efefef;
+}
+.markdown-section p.tip em {
+ color: #34495e;
+}
+.markdown-section p.warn {
+ background: rgba(66, 185, 131, 0.1);
+ border-radius: 2px;
+ padding: 1rem;
+}
+.markdown-section ul.task-list > li {
+ list-style-type: none;
+}
+body.close .sidebar {
+ transform: translateX(-300px);
+}
+body.close .sidebar-toggle {
+ width: auto;
+}
+body.close .content {
+ left: 0;
+}
+@media print {
+ .app-nav,
+ .github-corner,
+ .sidebar,
+ .sidebar-toggle {
+ display: none;
+ }
+}
+@media screen and (max-width: 768px) {
+ .github-corner,
+ .sidebar,
+ .sidebar-toggle {
+ position: fixed;
+ }
+ .app-nav {
+ margin-top: 16px;
+ }
+ .app-nav li ul {
+ top: 30px;
+ }
+ main {
+ height: auto;
+ min-height: 100vh;
+ overflow-x: hidden;
+ }
+ .sidebar {
+ left: -300px;
+ transition: transform 0.25s ease-out;
+ }
+ .content {
+ left: 0;
+ max-width: 100vw;
+ position: static;
+ padding-top: 20px;
+ transition: transform 0.25s ease;
+ }
+ .app-nav,
+ .github-corner {
+ transition: transform 0.25s ease-out;
+ }
+ .sidebar-toggle {
+ background-color: transparent;
+ width: auto;
+ padding: 30px 30px 10px 10px;
+ }
+ body.close .sidebar {
+ transform: translateX(300px);
+ }
+ body.close .sidebar-toggle {
+ background-color: hsla(0, 0%, 100%, 0.8);
+ transition: background-color 1s;
+ width: 284px;
+ padding: 10px;
+ }
+ body.close .content {
+ transform: translateX(300px);
+ }
+ body.close .app-nav,
+ body.close .github-corner {
+ display: none;
+ }
+ .github-corner:hover .octo-arm {
+ animation: none;
+ }
+ .github-corner .octo-arm {
+ animation: octocat-wave 0.56s ease-in-out;
+ }
+}
+@keyframes octocat-wave {
+ 0%,
+ to {
+ transform: rotate(0);
+ }
+ 20%,
+ 60% {
+ transform: rotate(-25deg);
+ }
+ 40%,
+ 80% {
+ transform: rotate(10deg);
+ }
+}
+section.cover {
+ position: relative;
+ align-items: center;
+ background-position: 50%;
+ background-repeat: no-repeat;
+ background-size: cover;
+ min-height: 100vh;
+ width: 100%;
+ display: none;
+}
+section.cover.show {
+ display: flex;
+}
+section.cover.has-mask .mask {
+ background-color: #fff;
+ opacity: 0.8;
+ position: absolute;
+ top: 0;
+ bottom: 0;
+ width: 100%;
+}
+section.cover .cover-main {
+ flex: 1;
+ margin: 0 16px;
+ text-align: center;
+ position: relative;
+}
+section.cover a {
+ color: inherit;
+}
+section.cover a,
+section.cover a:hover {
+ text-decoration: none;
+}
+section.cover p {
+ line-height: 1.5rem;
+ margin: 1em 0;
+}
+section.cover h1 {
+ color: inherit;
+ font-size: 2.5rem;
+ font-weight: 300;
+ margin: 0.625rem 0 2.5rem;
+ position: relative;
+ text-align: center;
+}
+section.cover h1 a {
+ display: block;
+}
+section.cover h1 small {
+ bottom: -0.4375rem;
+ font-size: 1rem;
+ position: absolute;
+}
+section.cover blockquote {
+ font-size: 1.5rem;
+ text-align: center;
+}
+section.cover ul {
+ line-height: 1.8;
+ list-style-type: none;
+ margin: 1em auto;
+ max-width: 500px;
+ padding: 0;
+}
+section.cover .cover-main > p:last-child a {
+ border-radius: 2rem;
+ border: 1px solid #673ab7;
+ border-color: var(--theme-color, #673ab7);
+ box-sizing: border-box;
+ color: #673ab7;
+ color: var(--theme-color, #673ab7);
+ display: inline-block;
+ font-size: 1.05rem;
+ letter-spacing: 0.1rem;
+ margin: 0.5rem 1rem;
+ padding: 0.75em 2rem;
+ text-decoration: none;
+ transition: all 0.15s ease;
+}
+section.cover .cover-main > p:last-child a:last-child {
+ background-color: #673ab7;
+ background-color: var(--theme-color, #673ab7);
+ color: #fff;
+}
+section.cover .cover-main > p:last-child a:last-child:hover {
+ color: inherit;
+ opacity: 0.8;
+}
+section.cover .cover-main > p:last-child a:hover {
+ color: inherit;
+}
+section.cover blockquote > p > a {
+ border-bottom: 2px solid #673ab7;
+ border-bottom: 2px solid var(--theme-color, #673ab7);
+ transition: color 0.3s;
+}
+section.cover blockquote > p > a:hover {
+ color: #673ab7;
+ color: var(--theme-color, #673ab7);
+}
+.sidebar,
+body {
+ background-color: #fefefe;
+}
+.sidebar {
+ color: #364149;
+}
+.sidebar li {
+ margin: 6px 0;
+}
+.sidebar ul li a {
+ color: #505d6b;
+ font-size: 14px;
+ font-weight: 400;
+ overflow: hidden;
+ text-decoration: none;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.sidebar ul li a:hover {
+ text-decoration: underline;
+}
+.sidebar ul li ul {
+ padding: 0;
+}
+.sidebar ul li.active > a {
+ border-right: 2px solid;
+ color: #673ab7;
+ color: var(--theme-color, #673ab7);
+ font-weight: 600;
+}
+.app-sub-sidebar li:before {
+ content: "-";
+ padding-right: 4px;
+ float: left;
+}
+.markdown-section h1,
+.markdown-section h2,
+.markdown-section h3,
+.markdown-section h4,
+.markdown-section strong {
+ color: #2c3e50;
+ font-weight: 600;
+}
+.markdown-section a {
+ color: #673ab7;
+ color: var(--theme-color, #673ab7);
+ font-weight: 600;
+}
+.markdown-section h1 {
+ font-size: 2rem;
+ margin: 0 0 1rem;
+}
+.markdown-section h2 {
+ font-size: 1.75rem;
+ margin: 45px 0 0.8rem;
+}
+.markdown-section h3 {
+ font-size: 1.5rem;
+ margin: 40px 0 0.6rem;
+}
+.markdown-section h4 {
+ font-size: 1.25rem;
+}
+.markdown-section h5 {
+ font-size: 1rem;
+}
+.markdown-section h6 {
+ color: #777;
+ font-size: 1rem;
+}
+.markdown-section figure,
+.markdown-section p {
+ margin: 1.2em 0;
+}
+.markdown-section ol,
+.markdown-section p,
+.markdown-section ul {
+ line-height: 1.6rem;
+ word-spacing: 0.05rem;
+}
+.markdown-section ol,
+.markdown-section ul {
+ padding-left: 1.5rem;
+}
+.markdown-section blockquote {
+ border-left: 4px solid #673ab7;
+ border-left: 4px solid var(--theme-color, #673ab7);
+ color: #858585;
+ margin: 2em 0;
+ padding-left: 20px;
+}
+.markdown-section blockquote p {
+ font-weight: 600;
+ margin-left: 0;
+}
+.markdown-section iframe {
+ margin: 1em 0;
+}
+.markdown-section em {
+ color: #7f8c8d;
+}
+.markdown-section code,
+.markdown-section output:after,
+.markdown-section pre {
+ font-family:
+ Roboto Mono,
+ Monaco,
+ courier,
+ monospace;
+}
+.markdown-section code,
+.markdown-section pre {
+ background-color: #f2f2f2;
+}
+.markdown-section output,
+.markdown-section pre {
+ margin: 1.2em 0;
+ position: relative;
+ border-radius: 6px;
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+}
+.markdown-section output,
+.markdown-section pre > code {
+ border-radius: 2px;
+ display: block;
+}
+.markdown-section output:after,
+.markdown-section pre > code {
+ -moz-osx-font-smoothing: initial;
+ -webkit-font-smoothing: initial;
+}
+.markdown-section code {
+ border-radius: 2px;
+ color: #ff4182;
+ margin: 0 2px;
+ padding: 3px 5px;
+ white-space: pre-wrap;
+}
+.markdown-section > :not(h1):not(h2):not(h3):not(h4):not(h5):not(h6) code {
+ font-size: 0.8rem;
+}
+.markdown-section pre {
+ padding: 0 1.4rem;
+ line-height: 1.5rem;
+ overflow: auto;
+ word-wrap: normal;
+}
+.markdown-section pre > code {
+ color: #525252;
+ font-size: 0.8rem;
+ padding: 2.2em 5px;
+ line-height: inherit;
+ margin: 0 2px;
+ max-width: inherit;
+ overflow: inherit;
+ white-space: inherit;
+}
+.markdown-section output {
+ padding: 1.7rem 1.4rem;
+ border: 1px dotted #ccc;
+}
+.markdown-section output > :first-child {
+ margin-top: 0;
+}
+.markdown-section output > :last-child {
+ margin-bottom: 0;
+}
+.markdown-section code:after,
+.markdown-section code:before,
+.markdown-section output:after,
+.markdown-section output:before {
+ letter-spacing: 0.05rem;
+}
+.markdown-section output:after,
+.markdown-section pre:after {
+ color: #ccc;
+ font-size: 0.6rem;
+ font-weight: 600;
+ height: 15px;
+ line-height: 15px;
+ padding: 5px 10px 0;
+ position: absolute;
+ right: 0;
+ text-align: right;
+ top: 0;
+ content: attr(data-lang);
+}
+.token.cdata,
+.token.comment,
+.token.doctype,
+.token.prolog {
+ color: #8e908c;
+}
+.token.namespace {
+ opacity: 0.7;
+}
+.token.boolean,
+.token.number {
+ color: #4caf50;
+}
+.token.punctuation {
+ color: #525252;
+}
+.token.property {
+ color: #c08b30;
+}
+.token.tag {
+ color: #673ab7;
+}
+.token.string {
+ color: #4caf50;
+ color: var(--theme-color, #4caf50);
+}
+.token.selector {
+ color: #6679cc;
+}
+.token.attr-name {
+ color: #2973b7;
+}
+.language-css .token.string,
+.style .token.string,
+.token.entity,
+.token.url {
+ color: #22a2c9;
+}
+.token.attr-value,
+.token.control,
+.token.directive,
+.token.unit {
+ color: #673ab7;
+ color: var(--theme-color, #673ab7);
+}
+.token.function,
+.token.keyword {
+ color: #ff4182;
+}
+.token.atrule,
+.token.regex,
+.token.statement {
+ color: #22a2c9;
+}
+.token.placeholder,
+.token.variable {
+ color: #3d8fd1;
+}
+.token.deleted {
+ text-decoration: line-through;
+}
+.token.inserted {
+ border-bottom: 1px dotted #202746;
+ text-decoration: none;
+}
+.token.italic {
+ font-style: italic;
+}
+.token.bold,
+.token.important {
+ font-weight: 700;
+}
+.token.important {
+ color: #c94922;
+}
+.token.entity {
+ cursor: help;
+}
+code .token {
+ -moz-osx-font-smoothing: initial;
+ -webkit-font-smoothing: initial;
+ min-height: 1.5rem;
+ position: relative;
+ left: auto;
+}
diff --git a/docs/index.html b/docs/index.html
new file mode 100644
index 0000000..7e3c5e3
--- /dev/null
+++ b/docs/index.html
@@ -0,0 +1,55 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8" />
+ <meta
+ name="viewport"
+ content="width=device-width, initial-scale=1, minimum-scale=1.0, shrink-to-fit=no, viewport-fit=cover"
+ />
+
+ <title>Mingling Documents</title>
+ <meta
+ name="Mingling Documents"
+ content="Quick start with Mingling and build your command-line program!"
+ />
+
+ <link rel="stylesheet" href="css/vue.css" />
+ </head>
+
+ <body>
+ <nav>
+ <a href="https://docs.rs/mingling/latest/mingling/">Docs.rs</a>
+ <a href="https://crates.io/crates/mingling">Crates.io</a>
+ |
+ <a href="https://github.com/CatilGrass/mingling">GitHub</a>
+ |
+ <a href="https://git.catilgrass.cn/catilgrass/mingling.git"
+ ><b>Source</b></a
+ >
+ </nav>
+ <div id="app"></div>
+
+ <script>
+ window.$docsify = {
+ name: "Mingling Document",
+ auto2top: true,
+ loadSidebar: true,
+ maxLevel: 0,
+ subMaxLevel: 3,
+ search: {
+ placeholder: "Search",
+ noData: "No matches found.",
+ depth: 2,
+ },
+ };
+ </script>
+
+ <script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/docsify.min.js"></script>
+
+ <script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/zoom-image.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/docsify@4/lib/plugins/search.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-bash.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-toml.min.js"></script>
+ <script src="https://cdn.jsdelivr.net/npm/prismjs@1/components/prism-rust.js"></script>
+ </body>
+</html>
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 @@
+<h1 align="center">Mingling's Basic Components</h1>
+<p align="center">
+ Table of Contents
+</p>
+
+---
+
+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 @@
+<h1 align="center">Program</h1>
+<p align="center">
+ Mingling's Basic Components
+</p>
+
+---
+
+## 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 @@
+<h1 align="center">Setup</h1>
+<p align="center">
+ Mingling's Basic Components
+</p>
+
+---
+
+## 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<ThisProgram, ThisProgram>
+ for MySetup
+{
+ fn setup(
+ &mut self,
+ program: &mut Program<ThisProgram, ThisProgram>
+ ) {
+ // 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<PathBuf>
+ = 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<ThisProgram, ThisProgram> for MySetup {
+ fn setup(
+ &mut self, program: &mut Program<ThisProgram, ThisProgram>
+ ) {
+ // 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<ThisProgram, ThisProgram>
+) {
+ // 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<PathBuf>
+ = 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<ThisProgram, ThisProgram>
+) {
+ 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 @@
+<h1 align="center">Dispatcher</h1>
+<p align="center">
+ Mingling's Basic Components
+</p>
+
+---
+
+
+
+
+## 💡 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 @@
+<h1 align="center">Chain</h1>
+<p align="center">
+ Mingling's Basic Components
+</p>
+
+---
+
+
+## 💡 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 @@
+<h1 align="center">Renderer</h1>
+<p align="center">
+ Mingling's Basic Components
+</p>
+
+---