forked from proxmox-mirrors/proxmox
		
	first succesful CLI test
Signed-off-by: Wolfgang Bumiller <wry.git@bumiller.com>
This commit is contained in:
		
							parent
							
								
									691af5cade
								
							
						
					
					
						commit
						ed3b7de2fd
					
				@ -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
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -9,6 +9,8 @@ use serde_json::Value;
 | 
			
		||||
 | 
			
		||||
use super::{ApiMethodInfo, ApiOutput, Parameter};
 | 
			
		||||
 | 
			
		||||
type MethodInfoRef<Body> = &'static (dyn ApiMethodInfo<Body> + Send + Sync);
 | 
			
		||||
 | 
			
		||||
/// A CLI root node.
 | 
			
		||||
pub struct App<Body: 'static> {
 | 
			
		||||
    name: &'static str,
 | 
			
		||||
@ -55,12 +57,20 @@ impl<Body: 'static> App<Body> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn resolve(&self, args: &[&str]) -> Result<ApiOutput<Body>, Error> {
 | 
			
		||||
    /// Resolve a list of parameters to a method and a parameter json value.
 | 
			
		||||
    pub fn resolve(&self, args: &[&str]) -> Result<(MethodInfoRef<Body>, 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<Body> {
 | 
			
		||||
        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<Body: 'static> Command<Body> {
 | 
			
		||||
        Command::SubCommands(SubCommands::new())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn resolve(&self, args: std::slice::Iter<&str>) -> Result<ApiOutput<Body>, Error> {
 | 
			
		||||
    fn resolve(&self, args: std::slice::Iter<&str>) -> Result<(MethodInfoRef<Body>, Value), Error> {
 | 
			
		||||
        match self {
 | 
			
		||||
            Command::Method(method) => method.resolve(args),
 | 
			
		||||
            Command::SubCommands(subcmd) => subcmd.resolve(args),
 | 
			
		||||
@ -120,7 +130,10 @@ impl<Body: 'static> SubCommands<Body> {
 | 
			
		||||
        self
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn resolve(&self, mut args: std::slice::Iter<&str>) -> Result<ApiOutput<Body>, Error> {
 | 
			
		||||
    fn resolve(
 | 
			
		||||
        &self,
 | 
			
		||||
        mut args: std::slice::Iter<&str>,
 | 
			
		||||
    ) -> Result<(MethodInfoRef<Body>, Value), Error> {
 | 
			
		||||
        match args.next() {
 | 
			
		||||
            None => bail!("missing subcommand"),
 | 
			
		||||
            Some(arg) => match self.commands.get(arg) {
 | 
			
		||||
@ -139,7 +152,7 @@ impl<Body: 'static> SubCommands<Body> {
 | 
			
		||||
// 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<Body: 'static> {
 | 
			
		||||
    pub method: &'static (dyn ApiMethodInfo<Body> + Send + Sync),
 | 
			
		||||
    pub method: MethodInfoRef<Body>,
 | 
			
		||||
    pub positional_args: &'static [&'static str],
 | 
			
		||||
    //pub formatter: Option<()>, // TODO: output formatter
 | 
			
		||||
}
 | 
			
		||||
@ -156,7 +169,10 @@ impl<Body: 'static> Method<Body> {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn resolve(&self, mut args: std::slice::Iter<&str>) -> Result<ApiOutput<Body>, Error> {
 | 
			
		||||
    fn resolve(
 | 
			
		||||
        &self,
 | 
			
		||||
        mut args: std::slice::Iter<&str>,
 | 
			
		||||
    ) -> Result<(MethodInfoRef<Body>, Value), Error> {
 | 
			
		||||
        let mut params = serde_json::Map::new();
 | 
			
		||||
        let mut positionals = self.positional_args.iter();
 | 
			
		||||
 | 
			
		||||
@ -210,7 +226,7 @@ impl<Body: 'static> Method<Body> {
 | 
			
		||||
            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<T> ParseCli for T {
 | 
			
		||||
    default fn parse_cli(name: &str, _value: Option<&str>) -> Result<Value, Error> {
 | 
			
		||||
        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<Value, Error> {
 | 
			
		||||
        // 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<Value, Error> {
 | 
			
		||||
        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<Value, Error> {
 | 
			
		||||
        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(),
 | 
			
		||||
        ))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<Bytes> {
 | 
			
		||||
        let foo = value["foo"].as_str().unwrap();
 | 
			
		||||
 | 
			
		||||
		Loading…
	
		Reference in New Issue
	
	Block a user