From 420e2381263bbcc12d18d841c3e10478e71a1961 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Tue, 18 Jun 2024 11:54:27 +0200 Subject: [PATCH] router: hook help/completion/docgen into new cli parser Signed-off-by: Wolfgang Bumiller --- proxmox-router/src/cli/command.rs | 7 +++- proxmox-router/src/cli/mod.rs | 69 +++++++++++++++++-------------- 2 files changed, 44 insertions(+), 32 deletions(-) diff --git a/proxmox-router/src/cli/command.rs b/proxmox-router/src/cli/command.rs index db018ca7..36fe829c 100644 --- a/proxmox-router/src/cli/command.rs +++ b/proxmox-router/src/cli/command.rs @@ -251,7 +251,7 @@ fn help_command( Ok(Value::Null) } -fn set_help_context(def: Option>) { +pub(crate) fn set_help_context(def: Option>) { HELP_CONTEXT.with(|ctx| { *ctx.borrow_mut() = def; }); @@ -339,7 +339,10 @@ pub fn handle_command( result } -fn prepare_cli_command(def: &CommandLineInterface, mut args: A) -> (String, Vec) +pub(crate) fn prepare_cli_command( + def: &CommandLineInterface, + mut args: A, +) -> (String, Vec) where A: Iterator, { diff --git a/proxmox-router/src/cli/mod.rs b/proxmox-router/src/cli/mod.rs index 85133fa3..b64cecac 100644 --- a/proxmox-router/src/cli/mod.rs +++ b/proxmox-router/src/cli/mod.rs @@ -15,10 +15,9 @@ use std::any::{Any, TypeId}; use std::collections::HashMap; use std::io::{self, Write}; +use std::sync::Arc; -use anyhow::{bail, Error}; - -use anyhow::{format_err, Error}; +use anyhow::{bail, format_err, Error}; use serde::Deserialize; use serde_json::Value; @@ -335,6 +334,7 @@ impl From for CommandLineInterface { } /// Options covering an entire hierarchy set of subcommands. +#[derive(Clone)] pub(crate) struct OptionEntry { schema: &'static Schema, parse: fn(env: &mut CliEnvironment, &mut HashMap) -> Result<(), Error>, @@ -398,24 +398,24 @@ impl OptionEntry { } } -pub struct CommandLine<'a> { - interface: &'a CommandLineInterface, +pub struct CommandLine { + interface: Arc, async_run: Option Result>, +} + +struct CommandLineParseState { prefix: String, global_option_schemas: HashMap<&'static str, &'static Schema>, global_option_values: HashMap, - global_option_types: HashMap, + global_option_types: HashMap, + async_run: Option Result>, } -impl<'cli> CommandLine<'cli> { - pub fn new(interface: &'cli CommandLineInterface) -> Self { +impl CommandLine { + pub fn new(interface: CommandLineInterface) -> Self { Self { - interface, + interface: Arc::new(interface), async_run: None, - prefix: String::new(), - global_option_schemas: HashMap::new(), - global_option_values: HashMap::new(), - global_option_types: HashMap::new(), } } @@ -424,23 +424,29 @@ impl<'cli> CommandLine<'cli> { self } - pub fn parse( - mut self, - rpcenv: &mut CliEnvironment, - args: A, - ) -> Result, Error> + pub fn parse(&self, rpcenv: &mut CliEnvironment, args: A) -> Result where A: IntoIterator, { - let cli = self.interface; - let mut args = args.into_iter(); - self.prefix = args - .next() - .expect("no parameters passed to CommandLine::parse"); - self.parse_do(cli, rpcenv, args.collect()) - } + let (prefix, args) = command::prepare_cli_command(&self.interface, args.into_iter()); - fn parse_do( + let state = CommandLineParseState { + prefix, + global_option_schemas: HashMap::new(), + global_option_values: HashMap::new(), + global_option_types: HashMap::new(), + async_run: self.async_run, + }; + + command::set_help_context(Some(Arc::clone(&self.interface))); + let out = state.parse_do(&self.interface, rpcenv, args); + command::set_help_context(None); + out + } +} + +impl CommandLineParseState { + fn parse_do<'cli>( self, cli: &'cli CommandLineInterface, rpcenv: &mut CliEnvironment, @@ -467,7 +473,7 @@ impl<'cli> CommandLine<'cli> { Ok(args) } - fn parse_nested( + fn parse_nested<'cli>( mut self, cli: &'cli CliCommandMap, rpcenv: &mut CliEnvironment, @@ -480,8 +486,11 @@ impl<'cli> CommandLine<'cli> { // handle possible "global" parameters for the current level: // first add the global args of this level to the known list: for entry in cli.global_options.values() { - self.global_option_types - .extend(cli.global_options.iter().map(|(id, entry)| (*id, entry))); + self.global_option_types.extend( + cli.global_options + .iter() + .map(|(id, entry)| (*id, entry.clone())), + ); for (name, schema) in entry.properties() { if self.global_option_schemas.insert(name, schema).is_some() { panic!( @@ -518,7 +527,7 @@ impl<'cli> CommandLine<'cli> { self.parse_do(sub_cmd, rpcenv, args) } - fn parse_simple( + fn parse_simple<'cli>( mut self, cli: &'cli CliCommand, rpcenv: &mut CliEnvironment,