diff --git a/proxmox-router/src/cli/getopts.rs b/proxmox-router/src/cli/getopts.rs index dcb0716e..8f697a49 100644 --- a/proxmox-router/src/cli/getopts.rs +++ b/proxmox-router/src/cli/getopts.rs @@ -263,6 +263,7 @@ pub(crate) struct ParseOptions<'t, 'o> { stop_at_positional: bool, stop_at_unknown: bool, deny_unknown: bool, + retain_unknown: bool, retain_separator: bool, } @@ -278,6 +279,7 @@ impl<'t, 'o> ParseOptions<'t, 'o> { stop_at_positional: false, stop_at_unknown: false, deny_unknown: false, + retain_unknown: false, retain_separator: false, } } @@ -288,6 +290,12 @@ impl<'t, 'o> ParseOptions<'t, 'o> { self } + /// Builder style option to retain unknown parameters in the returned argument array. + pub fn retain_unknown(mut self, deny: bool) -> Self { + self.retain_unknown = deny; + self + } + /// Builder style option to stop parsing on unknown parameters. /// This implies deny_unknown`. /// Useful for bash completion. @@ -351,12 +359,19 @@ impl<'t, 'o> ParseOptions<'t, 'o> { let opt_schema = self.option_schemas.get(option).copied(); - if self.deny_unknown && opt_schema.is_none() { - if self.stop_at_unknown { + if opt_schema.is_none() { + if self.retain_unknown { positional.push(orig_arg); - break; + continue; + } + + if self.deny_unknown { + if self.stop_at_unknown { + positional.push(orig_arg); + break; + } + errors.push(option.to_string(), format_err!("unknown option {option:?}")); } - errors.push(option.to_string(), format_err!("unknown option {option:?}")); } if let Some(value) = value { diff --git a/proxmox-router/src/cli/mod.rs b/proxmox-router/src/cli/mod.rs index 14f98b0c..3cc41ab3 100644 --- a/proxmox-router/src/cli/mod.rs +++ b/proxmox-router/src/cli/mod.rs @@ -464,11 +464,16 @@ impl<'cli> CommandLineParseState<'cli> { } /// Parse out the current global options and return the remaining `args`. - fn handle_current_global_options(&mut self, args: Vec) -> Result, Error> { + fn handle_current_global_options( + &mut self, + args: Vec, + needs_subcommand: bool, + ) -> Result, Error> { let mut global_args = Vec::new(); let args = getopts::ParseOptions::new(&mut global_args, &self.global_option_schemas) - .stop_at_positional(true) - .deny_unknown(true) + .deny_unknown(needs_subcommand) + .stop_at_positional(needs_subcommand) + .retain_unknown(!needs_subcommand) .parse(args)?; // and merge them into the hash map for (option, argument) in global_args { @@ -505,7 +510,7 @@ impl<'cli> CommandLineParseState<'cli> { self.enable_global_options(cli); - let mut args = self.handle_current_global_options(args)?; + let mut args = self.handle_current_global_options(args, true)?; // now deal with the actual subcommand list if args.is_empty() { @@ -538,7 +543,7 @@ impl<'cli> CommandLineParseState<'cli> { rpcenv: &mut CliEnvironment, args: Vec, ) -> Result, Error> { - let args = self.handle_current_global_options(args)?; + let args = self.handle_current_global_options(args, false)?; self.build_global_options(&mut *rpcenv)?; let interface = Arc::clone(&self.interface); Ok(Invocation {