src/cli: improve docs

This commit is contained in:
Dietmar Maurer 2019-12-01 16:41:49 +01:00
parent 3bf920527c
commit 28c855c0a2
5 changed files with 80 additions and 6 deletions

View File

@ -1,8 +1,16 @@
//! Tools to create command line parsers //! 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; mod environment;
pub use environment::*; pub use environment::*;
@ -29,17 +37,34 @@ use std::collections::HashMap;
use proxmox::api::ApiMethod; 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<String, String>) -> Vec<String>; pub type CompletionFunction = fn(&str, &HashMap<String, String>) -> Vec<String>;
/// Define a simple CLI command.
pub struct CliCommand { pub struct CliCommand {
/// The Schema definition.
pub info: &'static ApiMethod, 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 <value>`` pairs.
pub arg_param: &'static [&'static str], pub arg_param: &'static [&'static str],
/// Predefined parameters.
pub fixed_param: HashMap<&'static str, String>, 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<String, CompletionFunction>, pub completion_functions: HashMap<String, CompletionFunction>,
} }
impl CliCommand { impl CliCommand {
/// Create a new instance.
pub fn new(info: &'static ApiMethod) -> Self { pub fn new(info: &'static ApiMethod) -> Self {
Self { Self {
info, arg_param: &[], info, arg_param: &[],
@ -48,37 +73,46 @@ impl CliCommand {
} }
} }
/// Set argument parameter list.
pub fn arg_param(mut self, names: &'static [&'static str]) -> Self { pub fn arg_param(mut self, names: &'static [&'static str]) -> Self {
self.arg_param = names; self.arg_param = names;
self self
} }
/// Set fixed parameters.
pub fn fixed_param(mut self, key: &'static str, value: String) -> Self { pub fn fixed_param(mut self, key: &'static str, value: String) -> Self {
self.fixed_param.insert(key, value); self.fixed_param.insert(key, value);
self self
} }
/// Set completion functions.
pub fn completion_cb(mut self, param_name: &str, cb: CompletionFunction) -> Self { pub fn completion_cb(mut self, param_name: &str, cb: CompletionFunction) -> Self {
self.completion_functions.insert(param_name.into(), cb); self.completion_functions.insert(param_name.into(), cb);
self self
} }
} }
/// Define nested CLI commands.
pub struct CliCommandMap { pub struct CliCommandMap {
/// Each command has an unique name. The map associates names with
/// command definitions.
pub commands: HashMap<String, CommandLineInterface>, pub commands: HashMap<String, CommandLineInterface>,
} }
impl CliCommandMap { impl CliCommandMap {
/// Create a new instance.
pub fn new() -> Self { pub fn new() -> Self {
Self { commands: HashMap:: new() } Self { commands: HashMap:: new() }
} }
/// Insert another command.
pub fn insert<S: Into<String>>(mut self, name: S, cli: CommandLineInterface) -> Self { pub fn insert<S: Into<String>>(mut self, name: S, cli: CommandLineInterface) -> Self {
self.commands.insert(name.into(), cli); self.commands.insert(name.into(), cli);
self self
} }
/// Insert the help command.
pub fn insert_help(mut self) -> Self { pub fn insert_help(mut self) -> Self {
self.commands.insert(String::from("help"), help_command_def().into()); self.commands.insert(String::from("help"), help_command_def().into());
self self
@ -107,6 +141,7 @@ impl CliCommandMap {
} }
} }
/// Define Complex command line interfaces.
pub enum CommandLineInterface { pub enum CommandLineInterface {
Simple(CliCommand), Simple(CliCommand),
Nested(CliCommandMap), Nested(CliCommandMap),

View File

@ -14,12 +14,18 @@ use super::getopts;
use super::{CommandLineInterface, CliCommand, CliCommandMap, completion::*}; use super::{CommandLineInterface, CliCommand, CliCommandMap, completion::*};
use super::format::*; 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 = pub const OUTPUT_FORMAT: Schema =
StringSchema::new("Output format.") StringSchema::new("Output format.")
.format(&ApiStringFormat::Enum(&["text", "json", "json-pretty"])) .format(&ApiStringFormat::Enum(&["text", "json", "json-pretty"]))
.schema(); .schema();
pub fn handle_simple_command( fn handle_simple_command(
_top_def: &CommandLineInterface, _top_def: &CommandLineInterface,
prefix: &str, prefix: &str,
cli_cmd: &CliCommand, cli_cmd: &CliCommand,
@ -69,7 +75,7 @@ pub fn handle_simple_command(
Ok(()) Ok(())
} }
pub fn handle_nested_command( fn handle_nested_command(
top_def: &CommandLineInterface, top_def: &CommandLineInterface,
prefix: &str, prefix: &str,
def: &CliCommandMap, def: &CliCommandMap,
@ -172,11 +178,15 @@ fn set_help_context(def: Option<Arc<CommandLineInterface>>) {
HELP_CONTEXT.with(|ctx| { *ctx.borrow_mut() = def; }); 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) CliCommand::new(&API_METHOD_COMMAND_HELP)
.arg_param(&["command"]) .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( pub fn handle_command(
def: Arc<CommandLineInterface>, def: Arc<CommandLineInterface>,
prefix: &str, prefix: &str,
@ -199,6 +209,18 @@ pub fn handle_command(
result 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) { pub fn run_cli_command(def: CommandLineInterface) {
let def = match def { let def = match def {

View File

@ -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) { pub fn print_bash_completion(def: &CommandLineInterface) {
let comp_point: usize = match std::env::var("COMP_POINT") { 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( pub fn get_completions(
cmd_def: &CommandLineInterface, cmd_def: &CommandLineInterface,
line: &str, line: &str,

View File

@ -7,7 +7,7 @@ use proxmox::api::format::*;
use super::{CommandLineInterface, CliCommand, CliCommandMap}; 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 /// This is implemented for machine generatable formats 'json' and
/// 'json-pretty'. The 'text' format needs to be handled somewhere /// '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( pub fn generate_usage_str(
prefix: &str, prefix: &str,
cli_cmd: &CliCommand, cli_cmd: &CliCommand,
@ -115,6 +116,7 @@ pub fn generate_usage_str(
text text
} }
/// Print command usage for simple commands to ``stderr``.
pub fn print_simple_usage_error( pub fn print_simple_usage_error(
prefix: &str, prefix: &str,
cli_cmd: &CliCommand, cli_cmd: &CliCommand,
@ -124,6 +126,7 @@ pub fn print_simple_usage_error(
eprint!("Error: {}\nUsage: {}", err_msg, usage); eprint!("Error: {}\nUsage: {}", err_msg, usage);
} }
/// Print command usage for nested commands to ``stderr``.
pub fn print_nested_usage_error( pub fn print_nested_usage_error(
prefix: &str, prefix: &str,
def: &CliCommandMap, def: &CliCommandMap,
@ -133,6 +136,7 @@ pub fn print_nested_usage_error(
eprintln!("Error: {}\n\nUsage:\n\n{}", err_msg, usage); eprintln!("Error: {}\n\nUsage:\n\n{}", err_msg, usage);
} }
/// Helper to generate command usage text for nested commands.
pub fn generate_nested_usage( pub fn generate_nested_usage(
prefix: &str, prefix: &str,
def: &CliCommandMap, def: &CliCommandMap,
@ -163,6 +167,7 @@ pub fn generate_nested_usage(
usage usage
} }
/// Print help text to ``stderr``.
pub fn print_help( pub fn print_help(
top_def: &CommandLineInterface, top_def: &CommandLineInterface,
mut prefix: String, mut prefix: String,

View File

@ -2,6 +2,11 @@ use std::sync::Arc;
use super::*; use super::*;
/// Helper trait implementation for ``rustyline``.
///
/// This can be used to generate interactive commands using
/// ``rustyline`` (readline implementation).
///
pub struct CliHelper { pub struct CliHelper {
cmd_def: Arc<CommandLineInterface>, cmd_def: Arc<CommandLineInterface>,
} }