aboutsummaryrefslogtreecommitdiff
path: root/examples/example-completion/src/main.rs
blob: 7ca23b9fc1ebca4165808975e9db28b9eb3f7945 (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
126
127
128
129
//! 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 `builds` 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::{macros::suggest, prelude::*, ShellContext, Suggest};

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

    program.with_dispatcher(CMDGreet);

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

    // TIP: Note that the completion script reads stdout,
    // so make sure no output is produced before the CompletionDispatcher 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(["-r", "--repeat"])
        .pick_or((), "World")
        .unpack()
        .into();
    result
}

#[renderer]
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!();