aboutsummaryrefslogtreecommitdiff
path: root/examples/example-completion/src/main.rs
blob: 385387b00b70c9ca9535caf1352158d1c0065df3 (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
//! 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
//!    Enable the `comp` feature for `mingling` in `[dependencies]`
//!
//! 2. Generate completion scripts
//!    When the `comp` feature is enabled, `gen_program!()` automatically invokes
//!    `build_comp!()` at compile time, which generates the completion scripts
//!    (named after `CARGO_PKG_NAME`) into `target/mingling/`.
//!
//! 3. Verify
//!    Build your project with `cargo build`. The completion scripts will be generated in `target/mingling/`
//!
//!    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.previous_word == "-r" || ctx.previous_word == "--repeat" {
        return suggest! {}; // Don't suggest anything
    }

    // When the user is typing `-`
    if ctx.current_word.starts_with('-') {
        // Remove arguments that have already been typed by the user
        let typed: Vec<&str> = ctx.all_words.iter().map(String::as_str).collect();
        let mut set = suggest! {
            "-r": "Number of repetitions",
            "--repeat": "Number of repetitions",
        };
        if let Suggest::Suggest(items) = &mut set {
            items.retain(|item| !typed.contains(&item.suggest().as_str()));
        }
        return set;
    }

    // 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", EntryGreet);
#[derive(Grouped, Wrap)]
pub struct ResultName((u8, String));

#[chain]
fn handle_greet(args: EntryGreet) -> Next {
    let result: ResultName = args
        .pick_or(&arg![repeat: u8, 'r'], || 1)
        .pick_or(&arg![String], || "World".to_string())
        .unwrap()
        .into();
    result.into()
}

/// Renders the greeting with the result name and repeat count.
#[renderer]
fn render_name(result: ResultName) -> RenderResult {
    let (repeat, name) = result.0;
    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!();