src/cli/command.rs: add help command
This commit is contained in:
parent
8b6dd2240d
commit
698d9d4402
|
@ -185,5 +185,5 @@ fn main() {
|
||||||
.into()
|
.into()
|
||||||
);
|
);
|
||||||
|
|
||||||
run_cli_command(&cmd_def.into());
|
run_cli_command(cmd_def.into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -236,5 +236,5 @@ fn main() {
|
||||||
.insert("garbage-collect".to_owned(), garbage_collect_cmd_def.into())
|
.insert("garbage-collect".to_owned(), garbage_collect_cmd_def.into())
|
||||||
.insert("list".to_owned(), list_cmd_def.into());
|
.insert("list".to_owned(), list_cmd_def.into());
|
||||||
|
|
||||||
run_cli_command(&cmd_def.into());
|
run_cli_command(cmd_def.into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,5 +51,5 @@ fn main() {
|
||||||
.insert("datastore".to_owned(), datastore_commands())
|
.insert("datastore".to_owned(), datastore_commands())
|
||||||
.insert("garbage-collection".to_owned(), garbage_collection_commands());
|
.insert("garbage-collection".to_owned(), garbage_collection_commands());
|
||||||
|
|
||||||
run_cli_command(&cmd_def.into());
|
run_cli_command(cmd_def.into());
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use failure::*;
|
use failure::*;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
//use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::api_schema::*;
|
use crate::api_schema::*;
|
||||||
use crate::api_schema::router::*;
|
use crate::api_schema::router::*;
|
||||||
|
@ -63,14 +63,19 @@ fn get_property_description(
|
||||||
Schema::Array(ref schema) => (schema.description, None),
|
Schema::Array(ref schema) => (schema.description, None),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let default_text = match default {
|
||||||
|
Some(text) => format!(" (default={})", text),
|
||||||
|
None => String::new(),
|
||||||
|
};
|
||||||
|
|
||||||
if format == DocumentationFormat::ReST {
|
if format == DocumentationFormat::ReST {
|
||||||
|
|
||||||
let mut text = match style {
|
let mut text = match style {
|
||||||
ParameterDisplayStyle::Arg => {
|
ParameterDisplayStyle::Arg => {
|
||||||
format!(":``--{} {}``: ", name, type_text)
|
format!(":``--{} {}{}``: ", name, type_text, default_text)
|
||||||
}
|
}
|
||||||
ParameterDisplayStyle::Fixed => {
|
ParameterDisplayStyle::Fixed => {
|
||||||
format!(":``<{}> {}``: ", name, type_text)
|
format!(":``<{}> {}{}``: ", name, type_text, default_text)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -82,11 +87,6 @@ fn get_property_description(
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
let default_text = match default {
|
|
||||||
Some(text) => format!(" (default={})", text),
|
|
||||||
None => String::new(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let display_name = match style {
|
let display_name = match style {
|
||||||
ParameterDisplayStyle::Arg => {
|
ParameterDisplayStyle::Arg => {
|
||||||
format!("--{}", name)
|
format!("--{}", name)
|
||||||
|
@ -167,28 +167,30 @@ fn generate_usage_str(
|
||||||
done_hash.insert(prop);
|
done_hash.insert(prop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let option_indicator = if options.len() > 0 { " [OPTIONS]" } else { "" };
|
||||||
|
|
||||||
let mut text = match format {
|
let mut text = match format {
|
||||||
DocumentationFormat::Short => {
|
DocumentationFormat::Short => {
|
||||||
return format!("{}{}{}", indent, prefix, args);
|
return format!("{}{}{}{}\n\n", indent, prefix, args, option_indicator);
|
||||||
}
|
}
|
||||||
DocumentationFormat::Long => {
|
DocumentationFormat::Long => {
|
||||||
format!("{}{}{}\n", indent, prefix, args)
|
format!("{}{}{}{}\n\n", indent, prefix, args, option_indicator)
|
||||||
}
|
}
|
||||||
DocumentationFormat::Full => {
|
DocumentationFormat::Full => {
|
||||||
format!("{}{}{}\n\n{}\n", indent, prefix, args, description)
|
format!("{}{}{}{}\n\n{}\n\n", indent, prefix, args, option_indicator, description)
|
||||||
}
|
}
|
||||||
DocumentationFormat::ReST => {
|
DocumentationFormat::ReST => {
|
||||||
format!("``{} {}``\n\n{}\n\n", prefix, args.trim(), description)
|
format!("``{} {}{}``\n\n{}\n\n", prefix, args.trim(), option_indicator, description)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if arg_descr.len() > 0 {
|
if arg_descr.len() > 0 {
|
||||||
text.push('\n');
|
|
||||||
text.push_str(&arg_descr);
|
text.push_str(&arg_descr);
|
||||||
|
text.push('\n');
|
||||||
}
|
}
|
||||||
if options.len() > 0 {
|
if options.len() > 0 {
|
||||||
text.push('\n');
|
|
||||||
text.push_str(&options);
|
text.push_str(&options);
|
||||||
|
text.push('\n');
|
||||||
}
|
}
|
||||||
text
|
text
|
||||||
}
|
}
|
||||||
|
@ -199,7 +201,48 @@ fn print_simple_usage_error(prefix: &str, cli_cmd: &CliCommand, err: Error) {
|
||||||
eprint!("Error: {}\nUsage: {}", err, usage);
|
eprint!("Error: {}\nUsage: {}", err, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_simple_command(prefix: &str, cli_cmd: &CliCommand, args: Vec<String>) {
|
fn print_help(
|
||||||
|
top_def: &CommandLineInterface,
|
||||||
|
mut prefix: String,
|
||||||
|
args: &Vec<String>,
|
||||||
|
verbose: Option<bool>,
|
||||||
|
) {
|
||||||
|
let mut iface = top_def;
|
||||||
|
|
||||||
|
for cmd in args {
|
||||||
|
if let CommandLineInterface::Nested(map) = iface {
|
||||||
|
if let Some(subcmd) = find_command(map, cmd) {
|
||||||
|
iface = subcmd;
|
||||||
|
prefix.push(' ');
|
||||||
|
prefix.push_str(cmd);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eprintln!("no such command '{}'", cmd);
|
||||||
|
std::process::exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let format = match verbose.unwrap_or(false) {
|
||||||
|
true => DocumentationFormat::Full,
|
||||||
|
false => DocumentationFormat::Short,
|
||||||
|
};
|
||||||
|
|
||||||
|
match iface {
|
||||||
|
CommandLineInterface::Nested(map) => {
|
||||||
|
println!("Usage:\n\n{}", generate_nested_usage(&prefix, map, format));
|
||||||
|
}
|
||||||
|
CommandLineInterface::Simple(cli_cmd) => {
|
||||||
|
println!("Usage: {}", generate_usage_str(&prefix, cli_cmd, format, ""));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_simple_command(
|
||||||
|
top_def: &CommandLineInterface,
|
||||||
|
prefix: &str,
|
||||||
|
cli_cmd: &CliCommand,
|
||||||
|
args: Vec<String>,
|
||||||
|
) {
|
||||||
|
|
||||||
let (params, rest) = match getopts::parse_arguments(
|
let (params, rest) = match getopts::parse_arguments(
|
||||||
&args, &cli_cmd.arg_param, &cli_cmd.info.parameters) {
|
&args, &cli_cmd.arg_param, &cli_cmd.info.parameters) {
|
||||||
|
@ -210,6 +253,12 @@ fn handle_simple_command(prefix: &str, cli_cmd: &CliCommand, args: Vec<String>)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (cli_cmd.info.handler as *const fn()) == (dummy_help as *const fn()) {
|
||||||
|
let prefix = prefix.split(' ').next().unwrap().to_string();
|
||||||
|
print_help(top_def, prefix, &rest, params["verbose"].as_bool());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if !rest.is_empty() {
|
if !rest.is_empty() {
|
||||||
let err = format_err!("got additional arguments: {:?}", rest);
|
let err = format_err!("got additional arguments: {:?}", rest);
|
||||||
print_simple_usage_error(prefix, cli_cmd, err);
|
print_simple_usage_error(prefix, cli_cmd, err);
|
||||||
|
@ -252,9 +301,9 @@ fn find_command<'a>(def: &'a CliCommandMap, name: &str) -> Option<&'a CommandLin
|
||||||
|
|
||||||
fn print_nested_usage_error(prefix: &str, def: &CliCommandMap, err: Error) {
|
fn print_nested_usage_error(prefix: &str, def: &CliCommandMap, err: Error) {
|
||||||
|
|
||||||
let usage = generate_nested_usage(prefix, def, DocumentationFormat::Long);
|
let usage = generate_nested_usage(prefix, def, DocumentationFormat::Short);
|
||||||
|
|
||||||
eprintln!("Error: {}\n\nUsage:\n{}", err, usage);
|
eprintln!("Error: {}\n\nUsage:\n\n{}", err, usage);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_nested_usage(prefix: &str, def: &CliCommandMap, format: DocumentationFormat) -> String {
|
fn generate_nested_usage(prefix: &str, def: &CliCommandMap, format: DocumentationFormat) -> String {
|
||||||
|
@ -283,7 +332,12 @@ fn generate_nested_usage(prefix: &str, def: &CliCommandMap, format: Documentatio
|
||||||
usage
|
usage
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_nested_command(prefix: &str, def: &CliCommandMap, mut args: Vec<String>) {
|
fn handle_nested_command(
|
||||||
|
top_def: &CommandLineInterface,
|
||||||
|
prefix: &str,
|
||||||
|
def: &CliCommandMap,
|
||||||
|
mut args: Vec<String>,
|
||||||
|
) {
|
||||||
|
|
||||||
if args.len() < 1 {
|
if args.len() < 1 {
|
||||||
let mut cmds: Vec<&String> = def.commands.keys().collect();
|
let mut cmds: Vec<&String> = def.commands.keys().collect();
|
||||||
|
@ -315,10 +369,10 @@ fn handle_nested_command(prefix: &str, def: &CliCommandMap, mut args: Vec<String
|
||||||
|
|
||||||
match sub_cmd {
|
match sub_cmd {
|
||||||
CommandLineInterface::Simple(cli_cmd) => {
|
CommandLineInterface::Simple(cli_cmd) => {
|
||||||
handle_simple_command(&new_prefix, cli_cmd, args);
|
handle_simple_command(top_def, &new_prefix, cli_cmd, args);
|
||||||
}
|
}
|
||||||
CommandLineInterface::Nested(map) => {
|
CommandLineInterface::Nested(map) => {
|
||||||
handle_nested_command(&new_prefix, map, args);
|
handle_nested_command(top_def, &new_prefix, map, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -483,7 +537,22 @@ pub fn print_bash_completion(def: &CommandLineInterface) {
|
||||||
print_nested_completion(def, args);
|
print_nested_completion(def, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_cli_command(def: &CommandLineInterface) {
|
pub fn run_cli_command(def: CommandLineInterface) {
|
||||||
|
|
||||||
|
let help_cmd_def = CliCommand::new(
|
||||||
|
ApiMethod::new(
|
||||||
|
dummy_help,
|
||||||
|
ObjectSchema::new("Get help about specified command.")
|
||||||
|
.optional("verbose", BooleanSchema::new("Verbose help."))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
let def = match def {
|
||||||
|
CommandLineInterface::Simple(cli_cmd) => CommandLineInterface::Simple(cli_cmd),
|
||||||
|
CommandLineInterface::Nested(map) => CommandLineInterface::Nested(map.insert("help", help_cmd_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();
|
||||||
|
|
||||||
|
@ -494,17 +563,17 @@ pub fn run_cli_command(def: &CommandLineInterface) {
|
||||||
|
|
||||||
if !args.is_empty() {
|
if !args.is_empty() {
|
||||||
if args[0] == "bashcomplete" {
|
if args[0] == "bashcomplete" {
|
||||||
print_bash_completion(def);
|
print_bash_completion(&def);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if args[0] == "printdoc" {
|
if args[0] == "printdoc" {
|
||||||
let usage = match def {
|
let usage = match def {
|
||||||
CommandLineInterface::Simple(cli_cmd) => {
|
CommandLineInterface::Simple(cli_cmd) => {
|
||||||
generate_usage_str(&prefix, cli_cmd, DocumentationFormat::ReST, "")
|
generate_usage_str(&prefix, &cli_cmd, DocumentationFormat::ReST, "")
|
||||||
}
|
}
|
||||||
CommandLineInterface::Nested(map) => {
|
CommandLineInterface::Nested(map) => {
|
||||||
generate_nested_usage(&prefix, map, DocumentationFormat::ReST)
|
generate_nested_usage(&prefix, &map, DocumentationFormat::ReST)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
println!("{}", usage);
|
println!("{}", usage);
|
||||||
|
@ -513,8 +582,8 @@ pub fn run_cli_command(def: &CommandLineInterface) {
|
||||||
}
|
}
|
||||||
|
|
||||||
match def {
|
match def {
|
||||||
CommandLineInterface::Simple(cli_cmd) => handle_simple_command(&prefix, cli_cmd, args),
|
CommandLineInterface::Simple(ref cli_cmd) => handle_simple_command(top_def, &prefix, &cli_cmd, args),
|
||||||
CommandLineInterface::Nested(map) => handle_nested_command(&prefix, map, args),
|
CommandLineInterface::Nested(ref map) => handle_nested_command(top_def, &prefix, &map, args),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -557,6 +626,10 @@ pub struct CliCommandMap {
|
||||||
pub commands: HashMap<String, CommandLineInterface>,
|
pub commands: HashMap<String, CommandLineInterface>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn dummy_help(_param: Value, _info: &ApiMethod, _rpcenv: &mut RpcEnvironment) -> Result<Value, Error> {
|
||||||
|
panic!("internal error"); // this is just a place holder - never call this
|
||||||
|
}
|
||||||
|
|
||||||
impl CliCommandMap {
|
impl CliCommandMap {
|
||||||
|
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
|
|
Loading…
Reference in New Issue