clap/completions/
powershell.rs

1// Std
2use std::io::Write;
3
4// Internal
5use crate::{app::parser::Parser, INTERNAL_ERROR_MSG};
6
7pub struct PowerShellGen<'a, 'b>
8where
9    'a: 'b,
10{
11    p: &'b Parser<'a, 'b>,
12}
13
14impl<'a, 'b> PowerShellGen<'a, 'b> {
15    pub fn new(p: &'b Parser<'a, 'b>) -> Self {
16        PowerShellGen { p }
17    }
18
19    pub fn generate_to<W: Write>(&self, buf: &mut W) {
20        let bin_name = self.p.meta.bin_name.as_ref().unwrap();
21
22        let mut names = vec![];
23        let subcommands_cases = generate_inner(self.p, "", &mut names);
24
25        let result = format!(
26            r#"
27using namespace System.Management.Automation
28using namespace System.Management.Automation.Language
29
30Register-ArgumentCompleter -Native -CommandName '{bin_name}' -ScriptBlock {{
31    param($wordToComplete, $commandAst, $cursorPosition)
32
33    $commandElements = $commandAst.CommandElements
34    $command = @(
35        '{bin_name}'
36        for ($i = 1; $i -lt $commandElements.Count; $i++) {{
37            $element = $commandElements[$i]
38            if ($element -isnot [StringConstantExpressionAst] -or
39                $element.StringConstantType -ne [StringConstantType]::BareWord -or
40                $element.Value.StartsWith('-')) {{
41                break
42        }}
43        $element.Value
44    }}) -join ';'
45
46    $completions = @(switch ($command) {{{subcommands_cases}
47    }})
48
49    $completions.Where{{ $_.CompletionText -like "$wordToComplete*" }} |
50        Sort-Object -Property ListItemText
51}}
52"#,
53            bin_name = bin_name,
54            subcommands_cases = subcommands_cases
55        );
56
57        w!(buf, result.as_bytes());
58    }
59}
60
61// Escape string inside single quotes
62fn escape_string(string: &str) -> String {
63    string.replace("'", "''")
64}
65
66fn get_tooltip<T: ToString>(help: Option<&str>, data: T) -> String {
67    match help {
68        Some(help) => escape_string(help),
69        _ => data.to_string(),
70    }
71}
72
73fn generate_inner<'a, 'b, 'p>(
74    p: &'p Parser<'a, 'b>,
75    previous_command_name: &str,
76    names: &mut Vec<&'p str>,
77) -> String {
78    debugln!("PowerShellGen::generate_inner;");
79    let command_name = if previous_command_name.is_empty() {
80        p.meta.bin_name.as_ref().expect(INTERNAL_ERROR_MSG).clone()
81    } else {
82        format!("{};{}", previous_command_name, &p.meta.name)
83    };
84
85    let mut completions = String::new();
86    let preamble = String::from("\n            [CompletionResult]::new(");
87
88    for option in p.opts() {
89        if let Some(data) = option.s.short {
90            let tooltip = get_tooltip(option.b.help, data);
91            completions.push_str(&preamble);
92            completions.push_str(
93                format!(
94                    "'-{}', '{}', {}, '{}')",
95                    data, data, "[CompletionResultType]::ParameterName", tooltip
96                )
97                .as_str(),
98            );
99        }
100        if let Some(data) = option.s.long {
101            let tooltip = get_tooltip(option.b.help, data);
102            completions.push_str(&preamble);
103            completions.push_str(
104                format!(
105                    "'--{}', '{}', {}, '{}')",
106                    data, data, "[CompletionResultType]::ParameterName", tooltip
107                )
108                .as_str(),
109            );
110        }
111    }
112
113    for flag in p.flags() {
114        if let Some(data) = flag.s.short {
115            let tooltip = get_tooltip(flag.b.help, data);
116            completions.push_str(&preamble);
117            completions.push_str(
118                format!(
119                    "'-{}', '{}', {}, '{}')",
120                    data, data, "[CompletionResultType]::ParameterName", tooltip
121                )
122                .as_str(),
123            );
124        }
125        if let Some(data) = flag.s.long {
126            let tooltip = get_tooltip(flag.b.help, data);
127            completions.push_str(&preamble);
128            completions.push_str(
129                format!(
130                    "'--{}', '{}', {}, '{}')",
131                    data, data, "[CompletionResultType]::ParameterName", tooltip
132                )
133                .as_str(),
134            );
135        }
136    }
137
138    for subcommand in &p.subcommands {
139        let data = &subcommand.p.meta.name;
140        let tooltip = get_tooltip(subcommand.p.meta.about, data);
141        completions.push_str(&preamble);
142        completions.push_str(
143            format!(
144                "'{}', '{}', {}, '{}')",
145                data, data, "[CompletionResultType]::ParameterValue", tooltip
146            )
147            .as_str(),
148        );
149    }
150
151    let mut subcommands_cases = format!(
152        r"
153        '{}' {{{}
154            break
155        }}",
156        &command_name, completions
157    );
158
159    for subcommand in &p.subcommands {
160        let subcommand_subcommands_cases = generate_inner(&subcommand.p, &command_name, names);
161        subcommands_cases.push_str(&subcommand_subcommands_cases);
162    }
163
164    subcommands_cases
165}