// Std use std::io::Write; // Internal use app::parser::Parser; use INTERNAL_ERROR_MSG; pub struct PowerShellGen<'a, 'b> where 'a: 'b, { p: &'b Parser<'a, 'b>, } impl<'a, 'b> PowerShellGen<'a, 'b> { pub fn new(p: &'b Parser<'a, 'b>) -> Self { PowerShellGen { p: p } } pub fn generate_to(&self, buf: &mut W) { let bin_name = self.p.meta.bin_name.as_ref().unwrap(); let mut names = vec![]; let subcommands_cases = generate_inner(self.p, "", &mut names); let result = format!(r#" using namespace System.Management.Automation using namespace System.Management.Automation.Language Register-ArgumentCompleter -Native -CommandName '{bin_name}' -ScriptBlock {{ param($wordToComplete, $commandAst, $cursorPosition) $commandElements = $commandAst.CommandElements $command = @( '{bin_name}' for ($i = 1; $i -lt $commandElements.Count; $i++) {{ $element = $commandElements[$i] if ($element -isnot [StringConstantExpressionAst] -or $element.StringConstantType -ne [StringConstantType]::BareWord -or $element.Value.StartsWith('-')) {{ break }} $element.Value }}) -join ';' $completions = @(switch ($command) {{{subcommands_cases} }}) $completions.Where{{ $_.CompletionText -like "$wordToComplete*" }} | Sort-Object -Property ListItemText }} "#, bin_name = bin_name, subcommands_cases = subcommands_cases ); w!(buf, result.as_bytes()); } } // Escape string inside single quotes fn escape_string(string: &str) -> String { string.replace("'", "''") } fn get_tooltip(help: Option<&str>, data: T) -> String { match help { Some(help) => escape_string(help), _ => data.to_string() } } fn generate_inner<'a, 'b, 'p>( p: &'p Parser<'a, 'b>, previous_command_name: &str, names: &mut Vec<&'p str>, ) -> String { debugln!("PowerShellGen::generate_inner;"); let command_name = if previous_command_name.is_empty() { p.meta.bin_name.as_ref().expect(INTERNAL_ERROR_MSG).clone() } else { format!("{};{}", previous_command_name, &p.meta.name) }; let mut completions = String::new(); let preamble = String::from("\n [CompletionResult]::new("); for option in p.opts() { if let Some(data) = option.s.short { let tooltip = get_tooltip(option.b.help, data); completions.push_str(&preamble); completions.push_str(format!("'-{}', '{}', {}, '{}')", data, data, "[CompletionResultType]::ParameterName", tooltip).as_str()); } if let Some(data) = option.s.long { let tooltip = get_tooltip(option.b.help, data); completions.push_str(&preamble); completions.push_str(format!("'--{}', '{}', {}, '{}')", data, data, "[CompletionResultType]::ParameterName", tooltip).as_str()); } } for flag in p.flags() { if let Some(data) = flag.s.short { let tooltip = get_tooltip(flag.b.help, data); completions.push_str(&preamble); completions.push_str(format!("'-{}', '{}', {}, '{}')", data, data, "[CompletionResultType]::ParameterName", tooltip).as_str()); } if let Some(data) = flag.s.long { let tooltip = get_tooltip(flag.b.help, data); completions.push_str(&preamble); completions.push_str(format!("'--{}', '{}', {}, '{}')", data, data, "[CompletionResultType]::ParameterName", tooltip).as_str()); } } for subcommand in &p.subcommands { let data = &subcommand.p.meta.name; let tooltip = get_tooltip(subcommand.p.meta.about, data); completions.push_str(&preamble); completions.push_str(format!("'{}', '{}', {}, '{}')", data, data, "[CompletionResultType]::ParameterValue", tooltip).as_str()); } let mut subcommands_cases = format!( r" '{}' {{{} break }}", &command_name, completions ); for subcommand in &p.subcommands { let subcommand_subcommands_cases = generate_inner(&subcommand.p, &command_name, names); subcommands_cases.push_str(&subcommand_subcommands_cases); } subcommands_cases }