diff --git a/proxmox-api/src/api_type.rs b/proxmox-api/src/api_type.rs
index 23e12741..f5c238e5 100644
--- a/proxmox-api/src/api_type.rs
+++ b/proxmox-api/src/api_type.rs
@@ -49,7 +49,10 @@ impl Parameter {
let info = (self.type_info)();
match info.parse_cli {
Some(func) => func(name, value),
- None => bail!("cannot parse parameter '{}' as command line parameter", name),
+ None => bail!(
+ "cannot parse parameter '{}' as command line parameter",
+ name
+ ),
}
}
}
diff --git a/proxmox-api/src/cli.rs b/proxmox-api/src/cli.rs
index 11c84424..4ac4b6af 100644
--- a/proxmox-api/src/cli.rs
+++ b/proxmox-api/src/cli.rs
@@ -9,6 +9,8 @@ use serde_json::Value;
use super::{ApiMethodInfo, ApiOutput, Parameter};
+type MethodInfoRef
= &'static (dyn ApiMethodInfo + Send + Sync);
+
/// A CLI root node.
pub struct App {
name: &'static str,
@@ -55,12 +57,20 @@ impl App {
}
}
- pub fn resolve(&self, args: &[&str]) -> Result, Error> {
+ /// Resolve a list of parameters to a method and a parameter json value.
+ pub fn resolve(&self, args: &[&str]) -> Result<(MethodInfoRef, Value), Error> {
self.command
.as_ref()
.ok_or_else(|| format_err!("no commands available"))?
.resolve(args.iter())
}
+
+ /// Run a command through this command line interface.
+ pub fn run(&self, args: &[&str]) -> ApiOutput {
+ let (method, params) = self.resolve(args)?;
+ let handler = method.handler();
+ futures::executor::block_on(handler(params))
+ }
}
/// A node in the CLI command router. This is either
@@ -83,7 +93,7 @@ impl Command {
Command::SubCommands(SubCommands::new())
}
- fn resolve(&self, args: std::slice::Iter<&str>) -> Result, Error> {
+ fn resolve(&self, args: std::slice::Iter<&str>) -> Result<(MethodInfoRef, Value), Error> {
match self {
Command::Method(method) => method.resolve(args),
Command::SubCommands(subcmd) => subcmd.resolve(args),
@@ -120,7 +130,10 @@ impl SubCommands {
self
}
- fn resolve(&self, mut args: std::slice::Iter<&str>) -> Result, Error> {
+ fn resolve(
+ &self,
+ mut args: std::slice::Iter<&str>,
+ ) -> Result<(MethodInfoRef, Value), Error> {
match args.next() {
None => bail!("missing subcommand"),
Some(arg) => match self.commands.get(arg) {
@@ -139,7 +152,7 @@ impl SubCommands {
// XXX: If we want optional positional parameters - should we make an enum or just say the
// parameter name should have brackets around it?
pub struct Method {
- pub method: &'static (dyn ApiMethodInfo + Send + Sync),
+ pub method: MethodInfoRef,
pub positional_args: &'static [&'static str],
//pub formatter: Option<()>, // TODO: output formatter
}
@@ -156,7 +169,10 @@ impl Method {
}
}
- fn resolve(&self, mut args: std::slice::Iter<&str>) -> Result, Error> {
+ fn resolve(
+ &self,
+ mut args: std::slice::Iter<&str>,
+ ) -> Result<(MethodInfoRef, Value), Error> {
let mut params = serde_json::Map::new();
let mut positionals = self.positional_args.iter();
@@ -210,7 +226,7 @@ impl Method {
bail!("missing positional parameters: {}", missing);
}
- unreachable!();
+ Ok((self.method, Value::Object(params)))
}
/// This should insert the parameter 'arg' with value 'value' into 'params'.
@@ -276,7 +292,10 @@ pub trait ParseCli {
// Saves us another mass impl macro such as the one below:
impl ParseCli for T {
default fn parse_cli(name: &str, _value: Option<&str>) -> Result {
- bail!("invalid type for command line interface found for parameter '{}'", name);
+ bail!(
+ "invalid type for command line interface found for parameter '{}'",
+ name
+ );
}
}
@@ -300,24 +319,29 @@ impl_parse_cli_from_str! {isize, usize, i64, u64, i32, u32, i16, u16, i8, u8, f6
impl ParseCli for Value {
fn parse_cli(name: &str, _value: Option<&str>) -> Result {
// FIXME: we could of course allow generic json parameters...?
- bail!("found generic json parameter ('{}') in command line...", name);
+ bail!(
+ "found generic json parameter ('{}') in command line...",
+ name
+ );
}
}
impl ParseCli for &str {
fn parse_cli(name: &str, value: Option<&str>) -> Result {
- Ok(Value::String(value
- .ok_or_else(|| format_err!("missing value for parameter '{}'", name))?
- .to_string()
+ Ok(Value::String(
+ value
+ .ok_or_else(|| format_err!("missing value for parameter '{}'", name))?
+ .to_string(),
))
}
}
impl ParseCli for String {
fn parse_cli(name: &str, value: Option<&str>) -> Result {
- Ok(Value::String(value
- .ok_or_else(|| format_err!("missing value for parameter '{}'", name))?
- .to_string()
+ Ok(Value::String(
+ value
+ .ok_or_else(|| format_err!("missing value for parameter '{}'", name))?
+ .to_string(),
))
}
}
diff --git a/proxmox-api/tests/cli.rs b/proxmox-api/tests/cli.rs
index 0cb8809a..515623b2 100644
--- a/proxmox-api/tests/cli.rs
+++ b/proxmox-api/tests/cli.rs
@@ -16,19 +16,22 @@ fn simple() {
"newboth",
cli::Command::method(simple_method, &["foo", "bar"]),
);
+
+ let result = cli
+ .run(&["new", "--foo=FOO", "--bar=BAR"])
+ .expect("command should execute successfully");
+ let body =
+ std::str::from_utf8(result.body().as_ref()).expect("expected a valid utf8 repsonse body");
+ assert_eq!(body, "FOO:BAR");
}
mod methods {
use bytes::Bytes;
- use failure::{bail, Error};
use http::Response;
use lazy_static::lazy_static;
- use serde_derive::{Deserialize, Serialize};
use serde_json::Value;
- use proxmox_api::{
- get_type_info, ApiFuture, ApiMethod, ApiOutput, ApiType, Parameter, TypeInfo,
- };
+ use proxmox_api::{get_type_info, ApiFuture, ApiMethod, ApiOutput, ApiType, Parameter};
pub async fn simple_method(value: Value) -> ApiOutput {
let foo = value["foo"].as_str().unwrap();