]> git.proxmox.com Git - proxmox-backup.git/blobdiff - src/cli/command.rs
api: pass RpcEnvirnment to api handlers
[proxmox-backup.git] / src / cli / command.rs
index 1f81a9aa21c57df426bde4fb30e0c1f5dda2c7b4..b7e2f52f6ad952d7fae2caec8e9ef8ec310c2a1b 100644 (file)
@@ -2,17 +2,47 @@ use failure::*;
 use std::collections::HashMap;
 use std::collections::HashSet;
 
+use serde_json::Value;
+
 use crate::api::schema::*;
 use crate::api::router::*;
-use crate::api::config::*;
+//use crate::api::config::*;
 use crate::getopts;
 
+struct CliEnvironment {
+    result_attributes: HashMap<String, Value>,
+}
+
+impl CliEnvironment {
+    fn new() -> Self {
+        Self {  result_attributes: HashMap::new() }
+    }
+}
+
+impl RpcEnvironment for CliEnvironment {
+
+    fn set_result_attrib(&mut self, name: &str, value: Value) {
+        self.result_attributes.insert(name.into(), value);
+    }
+
+    fn get_result_attrib(&self, name: &str) -> Option<&Value> {
+        self.result_attributes.get(name)
+    }
+}
+
+
 pub fn print_cli_usage() {
 
     eprintln!("Usage: TODO");
 }
 
-fn handle_simple_command(cli_cmd: &CliCommand, args: Vec<String>) -> Result<(), Error> {
+#[derive(Debug, Fail)]
+#[fail(display = "Usage error: {}", _0)]
+pub struct UsageError(Error);
+
+pub struct Invocation<'a>(&'a CliCommand, Value);
+
+fn handle_simple_command(cli_cmd: &CliCommand, args: Vec<String>) -> Result<Invocation, Error> {
 
     let (params, rest) = getopts::parse_arguments(
         &args, &cli_cmd.arg_param, &cli_cmd.info.parameters)?;
@@ -21,11 +51,7 @@ fn handle_simple_command(cli_cmd: &CliCommand, args: Vec<String>) -> Result<(),
         bail!("got additional arguments: {:?}", rest);
     }
 
-    let res = (cli_cmd.info.handler)(params,  &cli_cmd.info)?;
-
-    println!("Result: {}", serde_json::to_string_pretty(&res).unwrap());
-
-    Ok(())
+    Ok(Invocation(cli_cmd, params))
 }
 
 fn find_command<'a>(def: &'a CliCommandMap, name: &str) -> Option<&'a CommandLineInterface> {
@@ -50,7 +76,7 @@ fn find_command<'a>(def: &'a CliCommandMap, name: &str) -> Option<&'a CommandLin
     None
 }
 
-fn handle_nested_command(def: &CliCommandMap, mut args: Vec<String>) -> Result<(), Error> {
+fn handle_nested_command(def: &CliCommandMap, mut args: Vec<String>) -> Result<Invocation, Error> {
 
     if args.len() < 1 {
         let mut cmds: Vec<&String> = def.commands.keys().collect();
@@ -74,18 +100,30 @@ fn handle_nested_command(def: &CliCommandMap, mut args: Vec<String>) -> Result<(
 
     match sub_cmd {
         CommandLineInterface::Simple(cli_cmd) => {
-            handle_simple_command(cli_cmd, args)?;
+            handle_simple_command(cli_cmd, args)
         }
         CommandLineInterface::Nested(map) => {
-            handle_nested_command(map, args)?;
+            handle_nested_command(map, args)
         }
     }
-
-    Ok(())
 }
 
-fn print_property_completion(schema: &Schema, arg: &str) {
-    // fixme: implement completion functions
+fn print_property_completion(
+    schema: &Schema,
+    name: &str,
+    completion_functions: &HashMap<String, CompletionFunction>,
+    arg: &str)
+{
+    if let Some(callback) = completion_functions.get(name) {
+        let list = (callback)(arg);
+        for value in list {
+            if value.starts_with(arg) {
+                println!("{}", value);
+            }
+        }
+        return;
+    }
+
     if let Schema::String(StringSchema { format: Some(format),  ..} ) = schema {
         if let ApiStringFormat::Enum(list) = format.as_ref() {
             for value in list {
@@ -130,12 +168,9 @@ fn print_simple_completion(
             args.remove(0);
             print_simple_completion(cli_cmd, done, &arg_param[1..], args);
             return;
-        }
-        if let Some((_, schema)) = cli_cmd.info.parameters.properties.get(prop_name) {
-            if args.is_empty() {
-                print_property_completion(schema, "");
-            } else {
-                print_property_completion(schema, &args[0]);
+        } else if args.len() == 1 {
+            if let Some((_, schema)) = cli_cmd.info.parameters.properties.get(prop_name) {
+                print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &args[0]);
             }
         }
         return;
@@ -152,13 +187,13 @@ fn print_simple_completion(
         if last.starts_with("--") && last.len() > 2 {
             let prop_name = &last[2..];
             if let Some((_, schema)) = cli_cmd.info.parameters.properties.get(prop_name) {
-                print_property_completion(schema, &prefix);
+                print_property_completion(schema, prop_name, &cli_cmd.completion_functions, &prefix);
             }
             return;
         }
     }
 
-    for (name, (optional, schema)) in &cli_cmd.info.parameters.properties {
+    for (name, (_optional, _schema)) in &cli_cmd.info.parameters.properties {
         if done.contains(*name) { continue; }
         let option = String::from("--") + name;
         if option.starts_with(&prefix) {
@@ -204,15 +239,15 @@ pub fn print_bash_completion(def: &CommandLineInterface) {
         Ok(val) => {
             match usize::from_str_radix(&val, 10) {
                 Ok(i) => i,
-                Err(e) => return,
+                Err(_) => return,
             }
         }
-        Err(e) => return,
+        Err(_) => return,
     };
 
     let cmdline = match std::env::var("COMP_LINE") {
         Ok(val) => val[0..comp_point].to_owned(),
-        Err(e) => return,
+        Err(_) => return,
     };
 
 
@@ -242,22 +277,40 @@ pub fn run_cli_command(def: &CommandLineInterface) -> Result<(), Error> {
         return Ok(());
     }
 
-    match def {
+    let invocation = match def {
         CommandLineInterface::Simple(cli_cmd) => handle_simple_command(cli_cmd, args),
         CommandLineInterface::Nested(map) => handle_nested_command(map, args),
-    }
+    };
+
+    let mut rpcenv = CliEnvironment::new();
+
+    let res = match invocation {
+        Err(e) => return Err(UsageError(e).into()),
+        Ok(invocation) => (invocation.0.info.handler)(invocation.1, &invocation.0.info, &mut rpcenv)?,
+    };
+
+    println!("Result: {}", serde_json::to_string_pretty(&res).unwrap());
+
+    Ok(())
 }
 
+pub type CompletionFunction = fn(&str) -> Vec<String>;
+
 pub struct CliCommand {
     pub info: ApiMethod,
     pub arg_param: Vec<&'static str>,
     pub fixed_param: Vec<&'static str>,
+    pub completion_functions: HashMap<String, CompletionFunction>,
 }
 
 impl CliCommand {
 
     pub fn new(info: ApiMethod) -> Self {
-        Self { info, arg_param: vec![], fixed_param: vec![] }
+        Self {
+            info, arg_param: vec![],
+            fixed_param: vec![],
+            completion_functions: HashMap::new(),
+        }
     }
 
     pub fn arg_param(mut self, names: Vec<&'static str>) -> Self {
@@ -269,6 +322,11 @@ impl CliCommand {
         self.fixed_param = args;
         self
     }
+
+    pub fn completion_cb(mut self, param_name: &str, cb:  CompletionFunction) -> Self {
+        self.completion_functions.insert(param_name.into(), cb);
+        self
+    }
 }
 
 pub struct CliCommandMap {