]> git.proxmox.com Git - proxmox-backup.git/commitdiff
src/cli/command.rs: add help command
authorDietmar Maurer <dietmar@proxmox.com>
Sat, 23 Feb 2019 14:10:48 +0000 (15:10 +0100)
committerDietmar Maurer <dietmar@proxmox.com>
Sat, 23 Feb 2019 14:10:48 +0000 (15:10 +0100)
src/bin/catar.rs
src/bin/proxmox-backup-client.rs
src/bin/proxmox-backup-manager.rs
src/cli/command.rs

index f6639cb7dcaacd766bf09d7f25043823bb28fa2d..6a2f8c401e5fe4b1838c0358adeaa9110d5bc777 100644 (file)
@@ -185,5 +185,5 @@ fn main() {
             .into()
         );
 
-    run_cli_command(&cmd_def.into());
+    run_cli_command(cmd_def.into());
 }
index eaafb2defc4430196cae0e42208661fa16f33838..263543952b8da076fc2d061672f0683d1c4f7c72 100644 (file)
@@ -236,5 +236,5 @@ fn main() {
         .insert("garbage-collect".to_owned(), garbage_collect_cmd_def.into())
         .insert("list".to_owned(), list_cmd_def.into());
 
-    run_cli_command(&cmd_def.into());
+    run_cli_command(cmd_def.into());
 }
index 05e2af06562e6669f1b8954265a0378f924d4894..0c315e2bd68eda35cc840a3c92519ddcfca9a0b6 100644 (file)
@@ -51,5 +51,5 @@ fn main() {
         .insert("datastore".to_owned(), datastore_commands())
         .insert("garbage-collection".to_owned(), garbage_collection_commands());
 
-    run_cli_command(&cmd_def.into());
+    run_cli_command(cmd_def.into());
 }
index 0c6ed10c8418d9781f74f573e0aa1f4e261cfa9f..dc61e023a55a161a511d71b0a7657841015b9aff 100644 (file)
@@ -1,7 +1,7 @@
 use failure::*;
 use std::collections::HashMap;
 use std::collections::HashSet;
-//use serde_json::Value;
+use serde_json::Value;
 
 use crate::api_schema::*;
 use crate::api_schema::router::*;
@@ -63,14 +63,19 @@ fn get_property_description(
         Schema::Array(ref schema) => (schema.description, None),
     };
 
+    let default_text = match default {
+        Some(text) =>  format!("   (default={})", text),
+        None => String::new(),
+    };
+
     if format == DocumentationFormat::ReST {
 
         let mut text = match style {
            ParameterDisplayStyle::Arg => {
-                format!(":``--{} {}``:  ", name, type_text)
+                format!(":``--{} {}{}``:  ", name, type_text, default_text)
             }
             ParameterDisplayStyle::Fixed => {
-                format!(":``<{}> {}``:  ", name, type_text)
+                format!(":``<{}> {}{}``:  ", name, type_text, default_text)
             }
         };
 
@@ -82,11 +87,6 @@ fn get_property_description(
 
     } else {
 
-        let default_text = match default {
-            Some(text) =>  format!("   (default={})", text),
-            None => String::new(),
-        };
-
         let display_name = match style {
             ParameterDisplayStyle::Arg => {
                 format!("--{}", name)
@@ -167,28 +167,30 @@ fn generate_usage_str(
         done_hash.insert(prop);
     }
 
+    let option_indicator = if options.len() > 0 { " [OPTIONS]" } else { "" };
+
     let mut text = match format {
         DocumentationFormat::Short => {
-            return format!("{}{}{}", indent, prefix, args);
+            return format!("{}{}{}{}\n\n", indent, prefix, args, option_indicator);
         }
         DocumentationFormat::Long => {
-            format!("{}{}{}\n", indent, prefix, args)
+            format!("{}{}{}{}\n\n", indent, prefix, args, option_indicator)
         }
         DocumentationFormat::Full => {
-            format!("{}{}{}\n\n{}\n", indent, prefix, args, description)
+            format!("{}{}{}{}\n\n{}\n\n", indent, prefix, args, option_indicator, description)
         }
         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 {
-        text.push('\n');
         text.push_str(&arg_descr);
+        text.push('\n');
     }
     if options.len() > 0 {
-        text.push('\n');
         text.push_str(&options);
+        text.push('\n');
     }
     text
 }
@@ -199,7 +201,48 @@ fn print_simple_usage_error(prefix: &str, cli_cmd: &CliCommand, err: Error) {
     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(
         &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() {
         let err = format_err!("got additional arguments: {:?}", rest);
         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) {
 
-    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 {
@@ -283,7 +332,12 @@ fn generate_nested_usage(prefix: &str, def: &CliCommandMap, format: Documentatio
     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 {
         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 {
         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) => {
-            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);
 }
 
-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();
 
@@ -494,17 +563,17 @@ pub fn run_cli_command(def: &CommandLineInterface) {
 
     if !args.is_empty() {
         if args[0] == "bashcomplete" {
-            print_bash_completion(def);
+            print_bash_completion(&def);
             return;
         }
 
         if args[0] == "printdoc" {
             let usage = match def {
                 CommandLineInterface::Simple(cli_cmd) => {
-                    generate_usage_str(&prefix, cli_cmd,  DocumentationFormat::ReST, "")
+                    generate_usage_str(&prefix, &cli_cmd,  DocumentationFormat::ReST, "")
                 }
                 CommandLineInterface::Nested(map) => {
-                    generate_nested_usage(&prefix, map, DocumentationFormat::ReST)
+                    generate_nested_usage(&prefix, &map, DocumentationFormat::ReST)
                 }
             };
             println!("{}", usage);
@@ -513,8 +582,8 @@ pub fn run_cli_command(def: &CommandLineInterface) {
     }
 
     match def {
-        CommandLineInterface::Simple(cli_cmd) => handle_simple_command(&prefix, cli_cmd, args),
-        CommandLineInterface::Nested(map) => handle_nested_command(&prefix, map, args),
+        CommandLineInterface::Simple(ref cli_cmd) => handle_simple_command(top_def, &prefix, &cli_cmd, 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>,
 }
 
+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 {
 
     pub fn new() -> Self {