src/cli/command.rs: implement help

This commit is contained in:
Dietmar Maurer 2019-11-30 12:57:02 +01:00
parent 8423c1fe64
commit 1201abcffa
3 changed files with 89 additions and 33 deletions

View File

@ -37,7 +37,6 @@ fn command_map() -> CliCommandMap {
cmd_def cmd_def
} }
fn main() -> Result<(), Error> { fn main() -> Result<(), Error> {
let def = CommandLineInterface::Nested(command_map()); let def = CommandLineInterface::Nested(command_map());
@ -58,14 +57,8 @@ fn main() -> Result<(), Error> {
let args = shellword_split(&line)?; let args = shellword_split(&line)?;
let def = helper.cmd_def(); let def = helper.cmd_def();
let _ = match def {
CommandLineInterface::Simple(ref cli_cmd) => { handle_command(def, "", args);
handle_simple_command(def, "", &cli_cmd, args)
}
CommandLineInterface::Nested(ref map) => {
handle_nested_command(def, "", &map, args)
}
};
} }
Ok(()) Ok(())

View File

@ -1,7 +1,10 @@
use failure::*; use failure::*;
use serde_json::Value; use serde_json::Value;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc;
use std::cell::RefCell;
use proxmox::api::*;
use proxmox::api::format::*; use proxmox::api::format::*;
use proxmox::api::schema::*; use proxmox::api::schema::*;
use proxmox::api::{ApiHandler, ApiMethod}; use proxmox::api::{ApiHandler, ApiMethod};
@ -321,16 +324,88 @@ pub fn print_bash_completion(def: &CommandLineInterface) {
} }
} }
const VERBOSE_HELP_SCHEMA: Schema = BooleanSchema::new("Verbose help.").schema(); const API_METHOD_COMMAND_HELP: ApiMethod = ApiMethod::new(
const COMMAND_HELP: ObjectSchema = ObjectSchema::new( &ApiHandler::Sync(&help_command),
"Get help about specified command.", &ObjectSchema::new(
&[ ("verbose", true, &VERBOSE_HELP_SCHEMA) ] "Get help about specified command.",
&[
( "command",
true,
&StringSchema::new("Command name.").schema()
),
( "verbose",
true,
&BooleanSchema::new("Verbose help.").schema()
),
],
)
); );
const API_METHOD_COMMAND_HELP: ApiMethod = ApiMethod::new_dummy(&COMMAND_HELP); std::thread_local! {
static HELP_CONTEXT: RefCell<Option<Arc<CommandLineInterface>>> = RefCell::new(None);
}
fn help_command(
param: Value,
_info: &ApiMethod,
_rpcenv: &mut dyn RpcEnvironment,
) -> Result<Value, Error> {
let command = param["command"].as_str();
let verbose = param["verbose"].as_bool();
HELP_CONTEXT.with(|ctx| {
match &*ctx.borrow() {
Some(def) => {
let mut args = Vec::new();
// TODO: Handle multilevel sub commands
if let Some(command) = command {
args.push(command.to_string());
}
print_help(def, String::from(""), &args, verbose);
}
None => {
eprintln!("Sorry, help context not set - internal error.");
}
}
});
Ok(Value::Null)
}
pub fn set_help_context(def: Option<Arc<CommandLineInterface>>) {
HELP_CONTEXT.with(|ctx| { *ctx.borrow_mut() = def; });
}
pub fn help_command_def() -> CliCommand { pub fn help_command_def() -> CliCommand {
CliCommand::new(&API_METHOD_COMMAND_HELP) CliCommand::new(&API_METHOD_COMMAND_HELP)
.arg_param(&["command"])
}
pub fn handle_command(
def: Arc<CommandLineInterface>,
prefix: &str,
args: Vec<String>,
) {
set_help_context(Some(def.clone()));
match &*def {
CommandLineInterface::Simple(ref cli_cmd) => {
if let Err(_) = handle_simple_command(&def, &prefix, &cli_cmd, args) {
std::process::exit(-1);
}
}
CommandLineInterface::Nested(ref map) => {
if let Err(_) = handle_nested_command(&def, &prefix, &map, args) {
std::process::exit(-1);
}
}
};
set_help_context(None);
} }
pub fn run_cli_command(def: CommandLineInterface) { pub fn run_cli_command(def: CommandLineInterface) {
@ -341,8 +416,6 @@ pub fn run_cli_command(def: CommandLineInterface) {
CommandLineInterface::Nested(map.insert("help", help_command_def().into())), CommandLineInterface::Nested(map.insert("help", help_command_def().into())),
}; };
let top_def = &def; // we pass this to the help function ...
let mut args = std::env::args(); let mut args = std::env::args();
let prefix = args.next().unwrap(); let prefix = args.next().unwrap();
@ -370,16 +443,5 @@ pub fn run_cli_command(def: CommandLineInterface) {
} }
} }
match def { handle_command(Arc::new(def), &prefix, args);
CommandLineInterface::Simple(ref cli_cmd) => {
if let Err(_) = handle_simple_command(top_def, &prefix, &cli_cmd, args) {
std::process::exit(-1);
}
}
CommandLineInterface::Nested(ref map) => {
if let Err(_) = handle_nested_command(top_def, &prefix, &map, args) {
std::process::exit(-1);
}
}
};
} }

View File

@ -1,4 +1,5 @@
use failure::*; use failure::*;
use std::sync::Arc;
use rustyline::completion::*; use rustyline::completion::*;
@ -196,17 +197,17 @@ pub fn get_completions(
} }
pub struct CliHelper { pub struct CliHelper {
cmd_def: CommandLineInterface, cmd_def: Arc<CommandLineInterface>,
} }
impl CliHelper { impl CliHelper {
pub fn new(cmd_def: CommandLineInterface) -> Self { pub fn new(cmd_def: CommandLineInterface) -> Self {
Self { cmd_def } Self { cmd_def: Arc::new(cmd_def) }
} }
pub fn cmd_def(&self) -> &CommandLineInterface { pub fn cmd_def(&self) -> Arc<CommandLineInterface> {
&self.cmd_def self.cmd_def.clone()
} }
} }
@ -222,7 +223,7 @@ impl rustyline::completion::Completer for CliHelper {
let line = &line[..pos]; let line = &line[..pos];
let (start, completions) = super::get_completions(&self.cmd_def, line, false); let (start, completions) = super::get_completions(&*self.cmd_def, line, false);
return Ok((start, completions)); return Ok((start, completions));
} }