[pbs-devel] [PATCH proxmox 02/18] schema: support optional return values

Wolfgang Bumiller w.bumiller at proxmox.com
Fri Dec 18 12:25:50 CET 2020


Signed-off-by: Wolfgang Bumiller <w.bumiller at proxmox.com>
---
 proxmox/src/api/cli/format.rs |  9 +++++++--
 proxmox/src/api/format.rs     | 13 +++++++++---
 proxmox/src/api/router.rs     | 37 ++++++++++++++++++++++++++++++-----
 3 files changed, 49 insertions(+), 10 deletions(-)

diff --git a/proxmox/src/api/cli/format.rs b/proxmox/src/api/cli/format.rs
index 61d530a..a4e7c02 100644
--- a/proxmox/src/api/cli/format.rs
+++ b/proxmox/src/api/cli/format.rs
@@ -5,6 +5,7 @@ use serde_json::Value;
 use std::collections::HashSet;
 
 use crate::api::format::*;
+use crate::api::router::ReturnType;
 use crate::api::schema::*;
 
 use super::{value_to_text, TableFormatOptions};
@@ -32,16 +33,20 @@ pub fn format_and_print_result(result: &Value, output_format: &str) {
 /// formatted tables with borders.
 pub fn format_and_print_result_full(
     result: &mut Value,
-    schema: &Schema,
+    return_type: &ReturnType,
     output_format: &str,
     options: &TableFormatOptions,
 ) {
+    if return_type.optional && result.is_null() {
+        return;
+    }
+
     if output_format == "json-pretty" {
         println!("{}", serde_json::to_string_pretty(&result).unwrap());
     } else if output_format == "json" {
         println!("{}", serde_json::to_string(&result).unwrap());
     } else if output_format == "text" {
-        if let Err(err) = value_to_text(std::io::stdout(), result, schema, options) {
+        if let Err(err) = value_to_text(std::io::stdout(), result, &return_type.schema, options) {
             eprintln!("unable to format result: {}", err);
         }
     } else {
diff --git a/proxmox/src/api/format.rs b/proxmox/src/api/format.rs
index 6916b26..eac2214 100644
--- a/proxmox/src/api/format.rs
+++ b/proxmox/src/api/format.rs
@@ -4,6 +4,7 @@ use anyhow::Error;
 
 use std::io::Write;
 
+use crate::api::router::ReturnType;
 use crate::api::schema::*;
 use crate::api::{ApiHandler, ApiMethod};
 
@@ -197,8 +198,14 @@ fn dump_api_parameters(param: &ObjectSchema) -> String {
     res
 }
 
-fn dump_api_return_schema(schema: &Schema) -> String {
-    let mut res = String::from("*Returns*: ");
+fn dump_api_return_schema(returns: &ReturnType) -> String {
+    let schema = &returns.schema;
+
+    let mut res = if returns.optional {
+        "*Returns* (optionally): ".to_string()
+    } else {
+        "*Returns*: ".to_string()
+    };
 
     let type_text = get_schema_type_text(schema, ParameterDisplayStyle::Config);
     res.push_str(&format!("**{}**\n\n", type_text));
@@ -243,7 +250,7 @@ fn dump_method_definition(method: &str, path: &str, def: Option<&ApiMethod>) ->
         Some(api_method) => {
             let param_descr = dump_api_parameters(api_method.parameters);
 
-            let return_descr = dump_api_return_schema(api_method.returns);
+            let return_descr = dump_api_return_schema(&api_method.returns);
 
             let mut method = method;
 
diff --git a/proxmox/src/api/router.rs b/proxmox/src/api/router.rs
index f08f9a8..9fb9ec1 100644
--- a/proxmox/src/api/router.rs
+++ b/proxmox/src/api/router.rs
@@ -398,6 +398,33 @@ pub struct ApiAccess {
     pub permission: &'static Permission,
 }
 
+#[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
+pub struct ReturnType {
+    /// A return type may be optional, meaning the method may return null or some fixed data.
+    ///
+    /// If true, the return type in pseudo openapi terms would be `"oneOf": [ "null", "T" ]`.
+    pub optional: bool,
+
+    /// The method's return type.
+    pub schema: &'static schema::Schema,
+}
+
+impl std::fmt::Debug for ReturnType {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        if self.optional {
+            write!(f, "optional {:?}", self.schema)
+        } else {
+            write!(f, "{:?}", self.schema)
+        }
+    }
+}
+
+impl ReturnType {
+    pub const fn new(optional: bool, schema: &'static Schema) -> Self {
+        Self { optional, schema }
+    }
+}
+
 /// This struct defines a synchronous API call which returns the result as json `Value`
 #[cfg_attr(feature = "test-harness", derive(Eq, PartialEq))]
 pub struct ApiMethod {
@@ -410,7 +437,7 @@ pub struct ApiMethod {
     /// Parameter type Schema
     pub parameters: &'static schema::ObjectSchema,
     /// Return type Schema
-    pub returns: &'static schema::Schema,
+    pub returns: ReturnType,
     /// Handler function
     pub handler: &'static ApiHandler,
     /// Access Permissions
@@ -433,7 +460,7 @@ impl ApiMethod {
         Self {
             parameters,
             handler,
-            returns: &NULL_SCHEMA,
+            returns: ReturnType::new(false, &NULL_SCHEMA),
             protected: false,
             reload_timezone: false,
             access: ApiAccess {
@@ -447,7 +474,7 @@ impl ApiMethod {
         Self {
             parameters,
             handler: &DUMMY_HANDLER,
-            returns: &NULL_SCHEMA,
+            returns: ReturnType::new(false, &NULL_SCHEMA),
             protected: false,
             reload_timezone: false,
             access: ApiAccess {
@@ -457,8 +484,8 @@ impl ApiMethod {
         }
     }
 
-    pub const fn returns(mut self, schema: &'static Schema) -> Self {
-        self.returns = schema;
+    pub const fn returns(mut self, returns: ReturnType) -> Self {
+        self.returns = returns;
 
         self
     }
-- 
2.20.1





More information about the pbs-devel mailing list