From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 099DD61CDC for ; Fri, 18 Dec 2020 12:37:52 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 07D192FBE0 for ; Fri, 18 Dec 2020 12:37:52 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id 13AA42F829 for ; Fri, 18 Dec 2020 12:37:33 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id 6881A43B4D for ; Fri, 18 Dec 2020 12:26:19 +0100 (CET) From: Wolfgang Bumiller To: pbs-devel@lists.proxmox.com Date: Fri, 18 Dec 2020 12:25:50 +0100 Message-Id: <20201218112608.6845-3-w.bumiller@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201218112608.6845-1-w.bumiller@proxmox.com> References: <20201218112608.6845-1-w.bumiller@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL -1.127 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment SPF_PASS -0.001 SPF: sender matches SPF record T_SPF_HELO_TEMPERROR 0.01 SPF: test of HELO record failed (temperror) Subject: [pbs-devel] [PATCH proxmox 02/18] schema: support optional return values X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 18 Dec 2020 11:37:52 -0000 Signed-off-by: Wolfgang Bumiller --- 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