src/cli: improve docs
This commit is contained in:
parent
3bf920527c
commit
28c855c0a2
39
src/cli.rs
39
src/cli.rs
|
@ -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),
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue