clap/completions/
powershell.rs

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