aboutsummaryrefslogtreecommitdiff
path: root/examples/example-completion/src/main.rs
blob: 29d6026e6900257c6ccde8f9ba0e62e4cb7060d4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! Example Completion
//!
//! > This example demonstrates how to use **Mingling** to create fully dynamic command-line completions
//!
//! ## 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 `build` and `comp` features for `mingling` in `[build-dependencies]`
//!
//! 2. Write `build.rs`
//!    Write the following in `build.rs`
//!
//! ```rust,ignore
//! fn main() {
//!     build_scripts();
//! }
//!
//! /// 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();
//! }
//! ```
//!
//! 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!
//! ```

use mingling::{ShellContext, Suggest, macros::suggest, prelude::*};
use std::io::Write;

fn main() {
    let program = ThisProgram::new();

    // TIP: Note that the completion script reads stdout,
    // so make sure no output is produced before the CMDCompletion is dispatched.
    program.exec_and_exit();
}

// --------- 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"
        };
    }

    // When the user is typing `--repeat`
    if ctx.filling_argument(["-r", "--repeat"]) {
        return suggest! {}; // Don't suggest anything
    }

    // 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);
    }

    // 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 ---------

dispatcher!("greet", CMDGreet => EntryGreet);
pack!(ResultName = (u8, String));

#[chain]
fn handle_greet(args: EntryGreet) -> Next {
    let result: ResultName = args
        .pick_or(["-r", "--repeat"], 1)
        .pick_or((), "World")
        .unpack()
        .into();
    result.into()
}

/// Renders the greeting with the result name and repeat count.
#[renderer]
fn render_name(result: ResultName) -> RenderResult {
    let (repeat, name) = result.inner;
    let mut render_result = RenderResult::new();
    let mut parts = Vec::with_capacity(repeat as usize);
    for _ in 0..repeat {
        parts.push(name.clone());
    }
    writeln!(render_result, "Hello, {}!", parts.join(", ")).ok();
    render_result
}

gen_program!();