diff --git a/src/cli.rs b/src/cli.rs index b2049180..2855aa43 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,16 @@ //! Tools to create command line parsers //! -//! We can use Schema deinititions to create command line parsers. +//! This crate provides convenient helpers to create command line +//! parsers using Schema definitions. //! -//! +//! ## Features +//! +//! - Use declarative API schema to define the CLI +//! - Automatic parameter verification +//! - Automatically generate documentation and manual pages +//! - Automatically generate bash completion helpers +//! - Ability to create interactive commands (using ``rustyline``) +//! - Supports complex/nested commands mod environment; pub use environment::*; @@ -29,17 +37,34 @@ use std::collections::HashMap; use proxmox::api::ApiMethod; +/// Completion function for single parameters. +/// +/// Completion functions gets the current parameter value, and should +/// return a list of all possible values. pub type CompletionFunction = fn(&str, &HashMap) -> Vec; +/// Define a simple CLI command. pub struct CliCommand { + /// The Schema definition. pub info: &'static ApiMethod, + /// Argument parameter list. + /// + /// Those parameters are expected to be passed as command line + /// arguments in the specified order. All other parameters needs + /// to be specified as ``--option `` pairs. pub arg_param: &'static [&'static str], + /// Predefined parameters. pub fixed_param: HashMap<&'static str, String>, + /// Completion functions. + /// + /// Each parameter may have an associated completion function, + /// which is called by the shell completion handler. pub completion_functions: HashMap, } impl CliCommand { + /// Create a new instance. pub fn new(info: &'static ApiMethod) -> Self { Self { info, arg_param: &[], @@ -48,37 +73,46 @@ impl CliCommand { } } + /// Set argument parameter list. pub fn arg_param(mut self, names: &'static [&'static str]) -> Self { self.arg_param = names; self } + /// Set fixed parameters. pub fn fixed_param(mut self, key: &'static str, value: String) -> Self { self.fixed_param.insert(key, value); self } + /// Set completion functions. pub fn completion_cb(mut self, param_name: &str, cb: CompletionFunction) -> Self { self.completion_functions.insert(param_name.into(), cb); self } } +/// Define nested CLI commands. pub struct CliCommandMap { + /// Each command has an unique name. The map associates names with + /// command definitions. pub commands: HashMap, } impl CliCommandMap { + /// Create a new instance. pub fn new() -> Self { Self { commands: HashMap:: new() } } + /// Insert another command. pub fn insert>(mut self, name: S, cli: CommandLineInterface) -> Self { self.commands.insert(name.into(), cli); self } + /// Insert the help command. pub fn insert_help(mut self) -> Self { self.commands.insert(String::from("help"), help_command_def().into()); self @@ -107,6 +141,7 @@ impl CliCommandMap { } } +/// Define Complex command line interfaces. pub enum CommandLineInterface { Simple(CliCommand), Nested(CliCommandMap), diff --git a/src/cli/command.rs b/src/cli/command.rs index 1cd3b1a0..857194f4 100644 --- a/src/cli/command.rs +++ b/src/cli/command.rs @@ -14,12 +14,18 @@ use super::getopts; use super::{CommandLineInterface, CliCommand, CliCommandMap, completion::*}; use super::format::*; +/// Schema definition for ``--output-format`` parameter. +/// +/// - ``text``: command specific text format. +/// - ``json``: JSON, single line. +/// - ``json-pretty``: JSON, human readable. +/// pub const OUTPUT_FORMAT: Schema = StringSchema::new("Output format.") .format(&ApiStringFormat::Enum(&["text", "json", "json-pretty"])) .schema(); -pub fn handle_simple_command( +fn handle_simple_command( _top_def: &CommandLineInterface, prefix: &str, cli_cmd: &CliCommand, @@ -69,7 +75,7 @@ pub fn handle_simple_command( Ok(()) } -pub fn handle_nested_command( +fn handle_nested_command( top_def: &CommandLineInterface, prefix: &str, def: &CliCommandMap, @@ -172,11 +178,15 @@ fn set_help_context(def: Option>) { HELP_CONTEXT.with(|ctx| { *ctx.borrow_mut() = def; }); } -pub fn help_command_def() -> CliCommand { +pub(crate) fn help_command_def() -> CliCommand { CliCommand::new(&API_METHOD_COMMAND_HELP) .arg_param(&["command"]) } +/// Handle command invocation. +/// +/// This command gets the command line ``args`` and tries to invoke +/// the corresponding API handler. pub fn handle_command( def: Arc, prefix: &str, @@ -199,6 +209,18 @@ pub fn handle_command( result } +/// Helper to get arguments and invoke the command. +/// +/// This helper reads arguments with ``std::env::args()``. The first +/// argument is assumed to be the program name, and is passed as ``prefix`` to +/// ``handle_command()``. +/// +/// This helper automatically add the help command, and two special +/// sub-command: +/// +/// - ``bashcomplete``: Output bash completions instead of running the command. +/// - ``printdoc``: Output ReST documentation. +/// pub fn run_cli_command(def: CommandLineInterface) { let def = match def { diff --git a/src/cli/completion.rs b/src/cli/completion.rs index 7a953ec4..da7e9c0b 100644 --- a/src/cli/completion.rs +++ b/src/cli/completion.rs @@ -189,6 +189,12 @@ fn get_nested_completion( } } +/// Helper to generate bash completions. +/// +/// This helper extracts the command line from environment variable +/// set by ``bash``, namely ``COMP_LINE`` and ``COMP_POINT``. This is +/// passed to ``get_completions()``. Returned values are printed to +/// ``stdout``. pub fn print_bash_completion(def: &CommandLineInterface) { let comp_point: usize = match std::env::var("COMP_POINT") { @@ -213,6 +219,7 @@ pub fn print_bash_completion(def: &CommandLineInterface) { } } +/// Compute possible completions for a partial command pub fn get_completions( cmd_def: &CommandLineInterface, line: &str, diff --git a/src/cli/format.rs b/src/cli/format.rs index d4e0145b..4158e6ec 100644 --- a/src/cli/format.rs +++ b/src/cli/format.rs @@ -7,7 +7,7 @@ use proxmox::api::format::*; use super::{CommandLineInterface, CliCommand, CliCommandMap}; -/// Helper function to format and print result +/// Helper function to format and print result. /// /// This is implemented for machine generatable formats 'json' and /// 'json-pretty'. The 'text' format needs to be handled somewhere @@ -26,6 +26,7 @@ pub fn format_and_print_result( } } +/// Helper to generate command usage text for simple commands. pub fn generate_usage_str( prefix: &str, cli_cmd: &CliCommand, @@ -115,6 +116,7 @@ pub fn generate_usage_str( text } +/// Print command usage for simple commands to ``stderr``. pub fn print_simple_usage_error( prefix: &str, cli_cmd: &CliCommand, @@ -124,6 +126,7 @@ pub fn print_simple_usage_error( eprint!("Error: {}\nUsage: {}", err_msg, usage); } +/// Print command usage for nested commands to ``stderr``. pub fn print_nested_usage_error( prefix: &str, def: &CliCommandMap, @@ -133,6 +136,7 @@ pub fn print_nested_usage_error( eprintln!("Error: {}\n\nUsage:\n\n{}", err_msg, usage); } +/// Helper to generate command usage text for nested commands. pub fn generate_nested_usage( prefix: &str, def: &CliCommandMap, @@ -163,6 +167,7 @@ pub fn generate_nested_usage( usage } +/// Print help text to ``stderr``. pub fn print_help( top_def: &CommandLineInterface, mut prefix: String, diff --git a/src/cli/readline.rs b/src/cli/readline.rs index 073091a9..79217263 100644 --- a/src/cli/readline.rs +++ b/src/cli/readline.rs @@ -2,6 +2,11 @@ use std::sync::Arc; use super::*; +/// Helper trait implementation for ``rustyline``. +/// +/// This can be used to generate interactive commands using +/// ``rustyline`` (readline implementation). +/// pub struct CliHelper { cmd_def: Arc, }