From 496a67846fb8e7968bfd1adb206f28fedb9470f6 Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Tue, 12 Mar 2019 14:39:51 +0100 Subject: [PATCH] src/cli/command.rs: pass parsed parameters to completion function --- src/bin/proxmox-backup-client.rs | 7 +++-- src/cli/command.rs | 53 +++++++++++++++++--------------- src/config/datastore.rs | 3 +- src/tools.rs | 4 ++- 4 files changed, 38 insertions(+), 29 deletions(-) diff --git a/src/bin/proxmox-backup-client.rs b/src/bin/proxmox-backup-client.rs index 06ba3a48..99deb745 100644 --- a/src/bin/proxmox-backup-client.rs +++ b/src/bin/proxmox-backup-client.rs @@ -4,7 +4,7 @@ use failure::*; //use std::os::unix::io::AsRawFd; use chrono::{DateTime, Local, TimeZone}; use std::path::{Path, PathBuf}; -use std::ffi::OsString; +use std::collections::HashMap; use proxmox_backup::tools; use proxmox_backup::cli::*; @@ -379,7 +379,7 @@ fn create_backup( Ok(Value::Null) } -pub fn complete_backup_source(arg: &str) -> Vec { +pub fn complete_backup_source(arg: &str, param: &HashMap) -> Vec { let mut result = vec![]; @@ -387,7 +387,7 @@ pub fn complete_backup_source(arg: &str) -> Vec { if data.len() != 2 { return result; } - let files = tools::complete_file_name(data[1]); + let files = tools::complete_file_name(data[1], param); for file in files { result.push(format!("{}:{}", data[0], file)); @@ -592,4 +592,5 @@ fn main() { .insert("snapshots".to_owned(), snapshots_cmd_def.into()); run_cli_command(cmd_def.into()); + } diff --git a/src/cli/command.rs b/src/cli/command.rs index 719f2853..e64ddeb6 100644 --- a/src/cli/command.rs +++ b/src/cli/command.rs @@ -399,10 +399,11 @@ fn print_property_completion( schema: &Schema, name: &str, completion_functions: &HashMap, - arg: &str) -{ + arg: &str, + param: &HashMap, +) { if let Some(callback) = completion_functions.get(name) { - let list = (callback)(arg); + let list = (callback)(arg, param); for value in list { if value.starts_with(arg) { println!("{}", value); @@ -424,24 +425,19 @@ fn print_property_completion( println!(""); } -fn record_done_arguments(done: &mut HashSet, parameters: &ObjectSchema, list: &[String]) { +fn record_done_argument(done: &mut HashMap, parameters: &ObjectSchema, key: &str, value: &str) { - for arg in list { - if arg.starts_with("--") && arg.len() > 2 { - let prop_name = arg[2..].to_owned(); - if let Some((_, schema)) = parameters.properties.get::(&prop_name) { - match schema.as_ref() { - Schema::Array(_) => { /* do nothing */ } - _ => { done.insert(prop_name); } - } - } + if let Some((_, schema)) = parameters.properties.get::(key) { + match schema.as_ref() { + Schema::Array(_) => { /* do nothing ?? */ } + _ => { done.insert(key.to_owned(), value.to_owned()); } } } } fn print_simple_completion( cli_cmd: &CliCommand, - done: &mut HashSet, + done: &mut HashMap, arg_param: &[&str], args: &[String], ) { @@ -450,20 +446,28 @@ fn print_simple_completion( if !arg_param.is_empty() { let prop_name = arg_param[0]; - done.insert(prop_name.into()); if args.len() > 1 { + record_done_argument(done, &cli_cmd.info.parameters, prop_name, &args[0]); print_simple_completion(cli_cmd, done, &arg_param[1..], &args[1..]); return; } else if args.len() == 1 { + record_done_argument(done, &cli_cmd.info.parameters, prop_name, &args[0]); if let Some((_, schema)) = cli_cmd.info.parameters.properties.get(prop_name) { - print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &args[0]); + print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &args[0], done); } } return; } if args.is_empty() { return; } - record_done_arguments(done, &cli_cmd.info.parameters, &args); + // Try to parse all argumnets but last, record args already done + if args.len() > 1 { + let mut errors = ParameterError::new(); // we simply ignore any parsing errors here + let (data, rest) = getopts::parse_argument_list(&args[0..args.len()-1], &cli_cmd.info.parameters, &mut errors); + for (key, value) in &data { + record_done_argument(done, &cli_cmd.info.parameters, key, value); + } + } let prefix = &args[args.len()-1]; // match on last arg @@ -473,14 +477,14 @@ fn print_simple_completion( if last.starts_with("--") && last.len() > 2 { let prop_name = &last[2..]; if let Some((_, schema)) = cli_cmd.info.parameters.properties.get(prop_name) { - print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &prefix); + print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &prefix, done); } return; } } for (name, (_optional, _schema)) in &cli_cmd.info.parameters.properties { - if done.contains(*name) { continue; } + if done.contains_key(*name) { continue; } let option = String::from("--") + name; if option.starts_with(prefix) { println!("{}", option); @@ -490,7 +494,7 @@ fn print_simple_completion( fn print_help_completion(def: &CommandLineInterface, help_cmd: &CliCommand, args: &[String]) { - let mut done = HashSet::new(); + let mut done = HashMap::new(); match def { CommandLineInterface::Simple(_) => { @@ -530,9 +534,10 @@ fn print_nested_completion(def: &CommandLineInterface, args: &[String]) { match def { CommandLineInterface::Simple(cli_cmd) => { - let mut done = HashSet::new(); - let fixed: Vec = cli_cmd.fixed_param.keys().map(|s| s.to_string()).collect(); - record_done_arguments(&mut done, &cli_cmd.info.parameters, &fixed); + let mut done: HashMap = HashMap::new(); + cli_cmd.fixed_param.iter().map(|(key, value)| { + record_done_argument(&mut done, &cli_cmd.info.parameters, &key, &value); + }); print_simple_completion(cli_cmd, &mut done, &cli_cmd.arg_param, args); return; } @@ -650,7 +655,7 @@ pub fn run_cli_command(def: CommandLineInterface) { }; } -pub type CompletionFunction = fn(&str) -> Vec; +pub type CompletionFunction = fn(&str, &HashMap) -> Vec; pub struct CliCommand { pub info: ApiMethod, diff --git a/src/config/datastore.rs b/src/config/datastore.rs index 3365e3da..a4329ab8 100644 --- a/src/config/datastore.rs +++ b/src/config/datastore.rs @@ -2,6 +2,7 @@ use failure::*; //use std::fs::{OpenOptions}; use std::io::Read; +use std::collections::HashMap; //use std::sync::Arc; use crate::tools; @@ -66,7 +67,7 @@ pub fn save_config(config: &SectionConfigData) -> Result<(), Error> { } // shell completion helper -pub fn complete_datastore_name(_arg: &str) -> Vec { +pub fn complete_datastore_name(_arg: &str, _param: &HashMap) -> Vec { match config() { Ok(data) => data.sections.iter().map(|(id,_)| id.to_string()).collect(), Err(_) => return vec![], diff --git a/src/tools.rs b/src/tools.rs index 5add70e9..c9415932 100644 --- a/src/tools.rs +++ b/src/tools.rs @@ -17,6 +17,8 @@ use std::time::Duration; use std::os::unix::io::RawFd; use std::os::unix::io::AsRawFd; +use std::collections::HashMap; + use serde_json::Value; pub mod timer; @@ -385,7 +387,7 @@ pub fn required_array_param<'a>(param: &'a Value, name: &str) -> Result Vec { +pub fn complete_file_name(arg: &str, _param: &HashMap) -> Vec { let mut result = vec![];