diff options
| author | Weicao-CatilGrass <1992414357@qq.com> | 2026-05-23 23:41:04 +0800 |
|---|---|---|
| committer | Weicao-CatilGrass <1992414357@qq.com> | 2026-05-23 23:49:34 +0800 |
| commit | 0a2ef958c0dca21d19e4ffc38ba5a7c4078e182a (patch) | |
| tree | c82fc4242ed393b132ba514eb434d722e7d9c387 /examples/example-completion/src/main.rs | |
| parent | ccab1940c019dfbfb7dfcbbe4cb927258933755f (diff) | |
Rework examples and add entry macro for testing
Diffstat (limited to 'examples/example-completion/src/main.rs')
| -rw-r--r-- | examples/example-completion/src/main.rs | 205 |
1 files changed, 99 insertions, 106 deletions
diff --git a/examples/example-completion/src/main.rs b/examples/example-completion/src/main.rs index 31528d1..7ca23b9 100644 --- a/examples/example-completion/src/main.rs +++ b/examples/example-completion/src/main.rs @@ -1,136 +1,129 @@ -//! `Mingling` Example - Completion +//! Example Completion //! -//! # How to Deploy -//! 1. Enable the `comp` feature -//! ```toml -//! [dependencies] -//! mingling = { version = "...", features = [ -//! "comp", // Enable this feature -//! "parser" -//! ] } -//! ``` +//! > This example demonstrates how to use **Mingling** to create fully dynamic command-line completions //! -//! 2. Add `mingling` as a build dependency, enabling the `builds` and `comp` features -//! ```toml -//! [build-dependencies] -//! mingling = { version = "...", features = [ -//! "builds", // Enable this feature for build scripts -//! "comp" -//! ] } -//! ``` +//! ## About Completion Scripts +//! +//! To make your completions work, you need to generate a completion script using Mingling's tools +//! +//! 1. Enable features +//! You need to enable the `builds` and `comp` features for `mingling` in `[build-dependencies]` +//! +//! 2. Write `build.rs` +//! Write the following in `build.rs` //! -//! 3. Write `build.rs` to generate completion scripts at compile time -//! ```ignore -//! use mingling::build::{build_comp_scripts, build_comp_scripts_with_bin_name}; +//! ```rust,ignore //! fn main() { -//! // Generate completion scripts for the current program, using the Cargo package name as the binary filename -//! build_comp_scripts(env!("CARGO_PKG_NAME")).unwrap(); +//! build_scripts(); +//! } //! -//! // Or, explicitly specify the binary filename -//! // build_comp_scripts("your_bin").unwrap(); +//! /// Generate completion scripts +//! fn build_scripts() { +//! // `env!("CARGO_PKG_NAME")` equals the crate name, which matches the binary name. +//! // If your binary name differs from the crate name, specify it explicitly. +//! mingling::build::build_comp_scripts( +//! // Your binary name: +//! env!("CARGO_PKG_NAME"), +//! ) +//! .unwrap(); //! } //! ``` //! -//! 4. Write `main.rs`, adding completion logic for your command entry point -//! 5. Execute `cargo install --path ./`, then run the corresponding completion script in your shell - -use mingling::prelude::*; -use mingling::{ - macros::{suggest, suggest_enum}, - parser::{PickableEnum, Picker}, - EnumTag, Groupped, ShellContext, Suggest, -}; - -// Define dispatcher `FruitCommand`, directing subcommand "fruit" to `FruitEntry` -dispatcher!("fruit", FruitCommand => FruitEntry); +//! 3. Verify +//! Build your project with `cargo build --release`. The completion scripts will be generated in `target/release/` +//! +//! Execute the script or have it be automatically sourced by your Shell +//! +//! Run: +//! ```bash +//! cargo run --manifest-path examples/example-completion/Cargo.toml --quiet -- greet Alice --repeat 3 +//! ``` +//! +//! Output: +//! ```plaintext +//! Hello, Alice, Alice, Alice! +//! ``` -#[completion(FruitEntry)] -fn comp_fruit_command(ctx: &ShellContext) -> Suggest { - if ctx.filling_argument_first("--name") { - return suggest!(); - } - if ctx.filling_argument_first("--type") { - return suggest_enum!(FruitType); - } - if ctx.typing_argument() { - return suggest! { - "--name": "Fruit name", - "--type": "Fruit type" - } - .strip_typed_argument(ctx); - } - return suggest!(); -} +use mingling::{macros::suggest, prelude::*, ShellContext, Suggest}; fn main() { let mut program = ThisProgram::new(); - program.with_dispatcher(CompletionDispatcher); - program.with_dispatcher(FruitCommand); - program.exec(); -} -#[derive(Groupped)] -struct FruitInfo { - name: String, - fruit_type: FruitType, -} + program.with_dispatcher(CMDGreet); -#[derive(Default, Debug, EnumTag)] -enum FruitType { - #[enum_desc("It's Apple")] - #[enum_rename("apple")] - FruitApple, + // --------- IMPORTANT --------- + // The `comp` feature makes `gen_program!()` generate a CompletionDispatcher automatically + // It adds a hidden `__comp` subcommand for communication with the completion script + program.with_dispatcher(crate::CompletionDispatcher); + // --------- IMPORTANT --------- - #[enum_desc("It's Banana")] - #[enum_rename("banana")] - FruitBanana, + // TIP: Note that the completion script reads stdout, + // so make sure no output is produced before the CompletionDispatcher is dispatched. + program.exec_and_exit(); +} - #[enum_desc("It's Cherry")] - #[enum_rename("cherry")] - FruitCherry, +// --------- IMPORTANT --------- +// __________________________________________ Entry point bound to completion behavior +// / _________________________ Shell context for obtaining user input state +// | / ________ Suggest, used to return completion results +// vvvvvvvvvv | / +#[completion(EntryGreet)] // vvvvvvvvvvvv vvvvvvv +fn complete_greet_entry(ctx: &ShellContext) -> Suggest { + // When the previous word is `greet` (the current command being typed) + if ctx.previous_word == "greet" { + // Return suggestions + return suggest! { + "Bob": "Likes to pass messages", + "Alice": "Likes to receive messages", + "Hacker": "YOU", + "World" + }; + } - #[enum_desc("It's Date")] - #[enum_rename("date")] - FruitDate, + // When the user is typing `--repeat` + if ctx.filling_argument(["-r", "--repeat"]) { + return suggest! {}; // Don't suggest anything + } - #[enum_desc("It's Elderberry")] - #[enum_rename("elderberry")] - FruitElderberry, + // When the user is typing `-` + if ctx.typing_argument() { + return suggest! { + "-r": "Number of repetitions", + "--repeat": "Number of repetitions", + } + // Remove arguments that have already been typed by the user + .strip_typed_argument(ctx); + } - #[default] - #[enum_rename("unknown")] - Unknown, + // Otherwise, suggest nothing + suggest!() + // // You can also enable file completions using the following code, + // // which will invoke the Shell's default behavior + // Suggest::file_comp() } +// --------- IMPORTANT --------- -impl PickableEnum for FruitType {} +dispatcher!("greet", CMDGreet => EntryGreet); +pack!(ResultName = (u8, String)); #[chain] -fn parse_fruit_info(prev: FruitEntry) -> Next { - let picker = Picker::from(prev.inner); - let (fruit_name, fruit_type) = picker.pick("--name").pick("--type").unpack(); - let info = FruitInfo { - name: fruit_name, - fruit_type, - }; - info.to_render() +fn handle_greet(args: EntryGreet) -> Next { + let result: ResultName = args + .pick(["-r", "--repeat"]) + .pick_or((), "World") + .unpack() + .into(); + result } #[renderer] -fn render_fruit(prev: FruitInfo) { - match (prev.name.is_empty(), prev.fruit_type) { - (true, FruitType::Unknown) => { - r_println!("Fruit name is empty and type is unknown"); - } - (true, fruit_type) => { - r_println!("Fruit name is empty, Type: {:?}", fruit_type); - } - (false, FruitType::Unknown) => { - r_println!("Fruit name: {}, Type is unknown", prev.name); - } - (false, fruit_type) => { - r_println!("Fruit name: {}, Type: {:?}", prev.name, fruit_type); - } +fn render_name(result: ResultName) { + let (repeat, name) = result.inner; + let mut parts = Vec::with_capacity(repeat as usize); + for _ in 0..repeat { + parts.push(name.clone()); } + r_println!("Hello, {}!", parts.join(", ")); } gen_program!(); |
