aboutsummaryrefslogtreecommitdiff
path: root/mingling
diff options
context:
space:
mode:
Diffstat (limited to 'mingling')
-rw-r--r--mingling/src/example_docs.rs382
-rw-r--r--mingling/src/example_docs.rs.tmpl18
-rw-r--r--mingling/src/lib.rs162
3 files changed, 404 insertions, 158 deletions
diff --git a/mingling/src/example_docs.rs b/mingling/src/example_docs.rs
new file mode 100644
index 0000000..ec8162e
--- /dev/null
+++ b/mingling/src/example_docs.rs
@@ -0,0 +1,382 @@
+// Auto generated
+
+/// `Mingling` Example - Basic
+///
+/// # How to Run
+/// ```bash
+/// cargo run --manifest-path ./examples/example-basic/Cargo.toml -- hello World
+/// ```
+///
+/// Cargo.toml
+/// ```
+/// [package]
+/// name = "example-basic"
+/// version = "0.0.1"
+/// edition = "2024"
+///
+/// [dependencies]
+/// mingling = { path = "../../mingling" }
+/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
+/// ```
+///
+/// main.rs
+/// ```rust
+/// use mingling::{
+/// macros::{chain, dispatcher, gen_program, pack, r_println, renderer},
+/// marker::NextProcess,
+/// };
+///
+/// // Define dispatcher `HelloCommand`, directing subcommand "hello" to `HelloEntry`
+/// dispatcher!("hello", HelloCommand => HelloEntry);
+///
+/// #[tokio::main]
+/// async fn main() {
+/// // Create program
+/// let mut program = ThisProgram::new();
+///
+/// // Add dispatcher `HelloCommand`
+/// program.with_dispatcher(HelloCommand);
+///
+/// // Run program
+/// program.exec().await;
+/// }
+///
+/// // Register wrapper type `Hello`, setting inner to `String`
+/// pack!(Hello = String);
+///
+/// // Register chain to `ThisProgram`, handling logic from `HelloEntry`
+/// #[chain]
+/// async fn parse_name(prev: HelloEntry) -> NextProcess {
+/// // Extract string from `HelloEntry` as argument
+/// let name = prev.first().cloned().unwrap_or_else(|| "World".to_string());
+///
+/// // Build `Hello` type and route to renderer
+/// Hello::new(name).to_render()
+/// }
+///
+/// // Register renderer to `ThisProgram`, handling rendering of `Hello`
+/// #[renderer]
+/// fn render_hello_who(prev: Hello) {
+/// // Print message
+/// r_println!("Hello, {}!", *prev);
+///
+/// // Program ends here
+/// }
+///
+/// // Generate program, default is `ThisProgram`
+/// gen_program!();
+/// ```
+pub mod example_basic {}
+/// `Mingling` Example - Completion
+///
+/// # How to Deploy
+/// 1. Enable the `comp` feature
+/// ```toml
+/// mingling = { version = "0.1.5", features = [
+/// "comp", // Enable this feature
+/// "parser"
+/// ] }
+/// ```
+///
+/// 2. Write `build.rs` to generate completion scripts at compile time
+/// ```rust
+/// use mingling::build::{build_comp_scripts, build_comp_scripts_with_bin_name};
+/// fn main() {
+/// // Generate completion scripts for the current program
+/// build_comp_scripts().unwrap();
+///
+/// // Or specify a specific name
+/// // build_comp_scripts_with_bin_name("your_bin").unwrap();
+/// }
+/// ```
+///
+/// 3. Write `main.rs`, adding completion logic for your command entry point
+/// 4. Execute `cargo install --path ./`, then run the corresponding completion script in your shell
+///
+/// Cargo.toml
+/// ```
+/// [package]
+/// name = "example-completion"
+/// version = "0.0.1"
+/// edition = "2024"
+///
+/// [dependencies]
+/// mingling = { path = "../../mingling", features = ["comp", "parser"] }
+/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
+/// ```
+///
+/// main.rs
+/// ```rust
+/// use mingling::{
+/// AnyOutput, Groupped, ShellContext, Suggest,
+/// macros::{chain, completion, dispatcher, gen_program, r_println, renderer, suggest},
+/// marker::NextProcess,
+/// parser::{Pickable, Picker},
+/// };
+///
+/// // Define dispatcher `FruitCommand`, directing subcommand "fruit" to `FruitEntry`
+/// dispatcher!("fruit", FruitCommand => FruitEntry);
+///
+/// #[completion(FruitEntry)]
+/// fn comp_fruit_command(ctx: &ShellContext) -> Suggest {
+/// if ctx.current_word.starts_with("-") {
+/// return suggest! {
+/// "--name": "Fruit name",
+/// "--type": "Fruit type"
+/// };
+/// }
+/// if ctx.previous_word == "--name" {
+/// return suggest!();
+/// }
+/// if ctx.previous_word == "--type" {
+/// return suggest! {
+/// "apple", "banana"
+/// };
+/// }
+/// return suggest!();
+/// }
+///
+/// #[tokio::main]
+/// async fn main() {
+/// let mut program = ThisProgram::new();
+/// program.with_dispatcher(CompletionDispatcher); // Add completion dispatcher
+/// program.with_dispatcher(FruitCommand);
+/// program.exec().await;
+/// }
+///
+/// #[derive(Groupped)]
+/// struct FruitInfo {
+/// name: String,
+/// fruit_type: FruitType,
+/// }
+///
+/// #[derive(Default, Debug)]
+/// enum FruitType {
+/// #[default]
+/// Apple,
+/// Banana,
+/// Other(String),
+/// }
+///
+/// impl Pickable for FruitType {
+/// type Output = FruitType;
+///
+/// fn pick(args: &mut mingling::parser::Argument, flag: mingling::Flag) -> Option<Self::Output> {
+/// let name = args.pick_argument(flag);
+/// match name {
+/// Some(name) => match name.as_str() {
+/// "apple" => Some(FruitType::Apple),
+/// "banana" => Some(FruitType::Banana),
+/// other => Some(FruitType::Other(other.to_string())),
+/// },
+/// None => None,
+/// }
+/// }
+/// }
+///
+/// #[chain]
+/// async fn parse_fruit_info(prev: FruitEntry) -> NextProcess {
+/// let picker = Picker::<ThisProgram>::from(prev.inner);
+/// let (fruit_name, fruit_type) = picker.pick("--name").pick("--type").unpack_directly();
+/// let info = FruitInfo {
+/// name: fruit_name,
+/// fruit_type,
+/// };
+/// AnyOutput::new(info).route_renderer()
+/// }
+///
+/// #[renderer]
+/// fn render_fruit(prev: FruitInfo) {
+/// if let FruitType::Other(other) = prev.fruit_type {
+/// r_println!("Fruit name: {}, Type: {:?} (Unknown)", prev.name, other);
+/// } else {
+/// r_println!("Fruit name: {}, Type: {:?}", prev.name, prev.fruit_type);
+/// }
+/// }
+///
+/// gen_program!();
+/// ```
+pub mod example_completion {}
+/// `Mingling` Example - General Renderer
+///
+/// ## Step1 - Enable Feature
+/// Enable the `general_renderer` feature for mingling in `Cargo.toml`
+/// ```toml
+/// [dependencies]
+/// mingling = { version = "...", features = ["general_renderer", "parser"] }
+/// ```
+///
+/// ## Step2 - Add Dependencies
+/// Add `serde` dependency to `Cargo.toml` for serialization support
+/// ```toml
+/// [dependencies]
+/// serde = { version = "1", features = ["derive"] }
+/// ```
+///
+/// ## Step3 - Write Code
+/// Write the following content into `main.rs`
+///
+/// ## Step3 - Build and Run
+/// ```bash
+/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22
+/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 --json
+/// cargo run --manifest-path ./examples/example-general-renderer/Cargo.toml -- render Bob 22 --yaml
+/// ```
+///
+/// Will print:
+/// ```plain
+/// Bob is 22 years old
+/// {"member_name":"Bob","member_age":22}
+/// member_name: Bob
+/// member_age: 22
+/// ```
+///
+/// Cargo.toml
+/// ```
+/// [package]
+/// name = "example-general-renderer"
+/// version = "0.0.1"
+/// edition = "2024"
+///
+/// [dependencies]
+/// mingling = { path = "../../mingling", features = [
+/// "parser",
+/// "general_renderer",
+/// ] }
+/// serde = { version = "1", features = ["derive"] }
+/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
+/// ```
+///
+/// main.rs
+/// ```rust
+/// use mingling::{
+/// AnyOutput, Groupped,
+/// macros::{chain, dispatcher, gen_program, r_println, renderer},
+/// marker::NextProcess,
+/// parser::Picker,
+/// setup::GeneralRendererSetup,
+/// };
+/// use serde::Serialize;
+///
+/// dispatcher!("render", RenderCommand => RenderCommandEntry);
+///
+/// #[tokio::main]
+/// async fn main() {
+/// let mut program = ThisProgram::new();
+/// // Add `GeneralRendererSetup` to receive user input `--json` `--yaml` parameters
+/// program.with_setup(GeneralRendererSetup);
+/// program.with_dispatcher(RenderCommand);
+/// program.exec().await;
+/// }
+///
+/// // Manually implement Info struct
+/// #[derive(Serialize, Groupped)]
+/// struct Info {
+/// #[serde(rename = "member_name")]
+/// name: String,
+/// #[serde(rename = "member_age")]
+/// age: i32,
+/// }
+///
+/// #[chain]
+/// async fn parse_render(prev: RenderCommandEntry) -> NextProcess {
+/// let (name, age) = Picker::<AnyOutput<ThisProgram>>::new(prev.inner)
+/// .pick::<String>(())
+/// .pick::<i32>(())
+/// .unpack_directly();
+/// AnyOutput::new(Info { name, age }).route_renderer()
+/// }
+///
+/// // Implement default renderer for when general_renderer is not specified
+/// #[renderer]
+/// fn render_info(prev: Info) {
+/// r_println!("{} is {} years old", prev.name, prev.age);
+/// }
+///
+/// gen_program!();
+/// ```
+pub mod example_general_renderer {}
+/// `Mingling` Example - Picker
+///
+/// ## Step1 - Enable Feature
+/// Enable the `parser` feature for mingling in `Cargo.toml`
+/// ```toml
+/// [dependencies]
+/// mingling = { version = "...", features = ["parser"] }
+/// ```
+///
+/// ## Step2 - Write Code
+/// Write the following content into `main.rs`
+///
+/// ## Step3 - Build and Run
+/// ```bash
+/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick Bob
+/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick Bob --age -15
+/// cargo run --manifest-path ./examples/example-picker/Cargo.toml -- pick --age 99
+/// ```
+///
+/// Cargo.toml
+/// ```
+/// [package]
+/// name = "example-picker"
+/// version = "0.0.1"
+/// edition = "2024"
+///
+/// [dependencies]
+/// mingling = { path = "../../mingling", features = ["parser"] }
+/// tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
+/// ```
+///
+/// main.rs
+/// ```rust
+/// use mingling::{
+/// AnyOutput,
+/// macros::{chain, dispatcher, gen_program, pack, r_println, renderer},
+/// marker::NextProcess,
+/// parser::Picker,
+/// };
+///
+/// dispatcher!("pick", PickCommand => PickEntry);
+///
+/// #[tokio::main]
+/// async fn main() {
+/// let mut program = ThisProgram::new();
+/// program.with_dispatcher(PickCommand);
+/// program.exec().await;
+/// }
+///
+/// pack!(NoNameProvided = ());
+/// pack!(ParsedPickInput = (i32, String));
+///
+/// #[chain]
+/// async fn parse(prev: PickEntry) -> NextProcess {
+/// // Extract arguments from `PickEntry`'s inner and create a `Picker`
+/// let picker = Picker::new(prev.inner);
+/// let picked = picker
+/// // First extract the named argument
+/// .pick_or("--age", 20)
+/// .after(|n: i32| n.clamp(0, 100))
+/// // Then sequentially extract the remaining arguments
+/// .pick_or_route((), AnyOutput::new(NoNameProvided::default()))
+/// .unpack();
+///
+/// match picked {
+/// Ok(value) => ParsedPickInput::new(value).to_render(),
+/// Err(e) => e.route_renderer(),
+/// }
+/// }
+///
+/// #[renderer]
+/// fn render_parsed_pick_input(prev: ParsedPickInput) {
+/// let (age, name) = prev.inner;
+/// r_println!("Picked: name = {}, age = {}", name, age);
+/// }
+///
+/// #[renderer]
+/// fn render_no_name_input(_prev: NoNameProvided) {
+/// r_println!("No name provided.");
+/// }
+///
+/// gen_program!();
+/// ```
+pub mod example_picker {} \ No newline at end of file
diff --git a/mingling/src/example_docs.rs.tmpl b/mingling/src/example_docs.rs.tmpl
new file mode 100644
index 0000000..5a32126
--- /dev/null
+++ b/mingling/src/example_docs.rs.tmpl
@@ -0,0 +1,18 @@
+// Auto generated
+
+>>>>>>>>>> examples
+
+@@@ >>> examples
+<<<example_header>>>
+///
+/// Cargo.toml
+/// ```
+<<<example_import>>>
+/// ```
+///
+/// main.rs
+/// ```rust
+<<<example_code>>>
+/// ```
+pub mod <<<example_name>>> {}
+@@@ <<<
diff --git a/mingling/src/lib.rs b/mingling/src/lib.rs
index 8d0a8c2..2bd6b58 100644
--- a/mingling/src/lib.rs
+++ b/mingling/src/lib.rs
@@ -101,163 +101,9 @@ pub mod macros {
/// derive macro Groupped
pub use mingling_macros::Groupped;
-pub mod docs {
- pub mod basic {
- //! # Basic Usage
- //!
- //! This module demonstrates basic usage of Mingling with a simple "hello" command.
- //!
- //! ## Example
- //!
- //! ```rust
- //! use mingling::{
- //! macros::{chain, dispatcher, gen_program, pack, r_println, renderer},
- //! marker::NextProcess,
- //! };
- //!
- //! // Define dispatcher `HelloCommand`, directing subcommand "hello" to `HelloEntry`
- //! dispatcher!("hello", HelloCommand => HelloEntry);
- //!
- //! #[tokio::main]
- //! async fn main() {
- //! // Create program
- //! let mut program = ThisProgram::new();
- //!
- //! // Add dispatcher `HelloCommand`
- //! program.with_dispatcher(HelloCommand);
- //!
- //! // Run program
- //! program.exec().await;
- //! }
- //!
- //! // Register wrapper type `Hello`, setting inner to `String`
- //! pack!(Hello = String);
- //!
- //! // Register chain to `ThisProgram`, handling logic from `HelloEntry`
- //! #[chain]
- //! async fn parse_name(prev: HelloEntry) -> NextProcess {
- //! // Extract string from `HelloEntry` as argument
- //! let name = prev.get(0).cloned().unwrap_or_else(|| "World".to_string());
- //!
- //! // Build `Hello` type and route to renderer
- //! Hello::new(name).to_render()
- //! }
- //!
- //! // Register renderer to `ThisProgram`, handling rendering of `Hello`
- //! #[renderer]
- //! fn render_hello_who(prev: Hello) {
- //! // Print message
- //! r_println!("Hello, {}!", *prev);
- //!
- //! // Program ends here
- //! }
- //!
- //! // Generate program, default is `ThisProgram`
- //! gen_program!();
- //! ```
- //!
- //! ## Output
- //!
- //! ```text
- //! > mycmd hello
- //! Hello, World!
- //! > mycmd hello Alice
- //! Hello, Alice!
- //! ```
- }
+mod example_docs;
- pub mod parser {
- //! # Feature `parser` Usage
- //!
- //! This module demonstrates advanced usage of Mingling with the `Picker` utility for argument parsing.
- //!
- //! ## Example
- //!
- //! ```rust
- //! use mingling::{
- //! AnyOutput,
- //! macros::{chain, dispatcher, gen_program, pack, r_println, renderer},
- //! marker::NextProcess,
- //! parser::Picker,
- //! };
- //!
- //! // Define dispatcher `RepeatCommand`, directing subcommand "repeat" to `RepeatEntry`
- //! dispatcher!("repeat", RepeatCommand => RepeatEntry);
- //!
- //! #[tokio::main]
- //! async fn main() {
- //! // Create program
- //! let mut program = ThisProgram::new();
- //!
- //! // Add dispatcher `RepeatCommand`
- //! program.with_dispatcher(RepeatCommand);
- //!
- //! // Run program
- //! program.exec().await;
- //! }
- //!
- //! // Register wrapper type `RepeatArgument`, setting inner to `(i32, String)`
- //! pack!(RepeatArgument = (i32, String));
- //!
- //! // Register error type
- //! pack!(ErrorContentRequired = ());
- //!
- //! // Register chain to `ThisProgram`, handling logic for `RepeatEntry`
- //! #[chain]
- //! async fn parse_repeat_args(prev: RepeatEntry) -> NextProcess {
- //! let picker = Picker::new(prev.inner); // Create Picker from user arguments
- //! let picked = picker
- //! .pick_or::<i32>("--time", 1) // Extract argument `--time`
- //! .after(|n| n.clamp(1, 20)) // Clamp extracted number between 1 - 20
- //! // Extract first remaining argument as content, route to type `ErrorContentRequired` if not found
- //! .pick_or_route((), AnyOutput::new(ErrorContentRequired::default()))
- //! .unpack(); // Unpack
- //!
- //! match picked {
- //! Ok(args) => {
- //! // Build `RepeatArgument` type and route to renderer
- //! RepeatArgument::new(args).to_render()
- //! }
- //! Err(e) => {
- //! // Extraction failed, route to error type
- //! e.route_renderer()
- //! }
- //! }
- //! }
- //!
- //! // Render `RepeatArgument`
- //! #[renderer]
- //! fn render_repeat(prev: RepeatArgument) {
- //! let (times, content) = prev.inner;
- //! for _ in 0..times {
- //! r_println!("{}", content);
- //! }
- //! }
- //!
- //! // Render `ErrorContentRequired`
- //! #[renderer]
- //! fn render_error_content_required(_prev: ErrorContentRequired) {
- //! r_println!("Error: content is required");
- //! }
- //!
- //! // Generate program, default is `ThisProgram`
- //! gen_program!();
- //! ```
- //!
- //! ## Output
- //!
- //! ```text
- //! > mycmd repeat --time 3 Hello
- //! Hello
- //! Hello
- //! Hello
- //! > mycmd repeat --time 25 Hello
- //! Hello
- //! Hello
- //! Hello
- //! ... (repeated 20 times, clamped from 25)
- //! > mycmd repeat --time 3
- //! Error: content is required
- //! ```
- }
+/// Example projects for `Mingling`, for learning how to use `Mingling`
+pub mod _mingling_examples {
+ pub use crate::example_docs::*;
}