From f5d15872f4b3551847c5a25566acad22ee4e1530 Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Thu, 19 Mar 2020 15:49:34 +0100 Subject: [PATCH] api-macro: add api_get_default!() macro When writing an #[api] function, one can now access default values by parameter name (see test_default_option in tests/options.rs): #[api(...)] pub fn func(value: Option) { println!( "value: {}", value.unwrap_or(api_get_default!("value")), ); } Signed-off-by: Wolfgang Bumiller --- proxmox-api-macro/src/api/method.rs | 40 +++++++++++++++++++++++++++++ proxmox-api-macro/tests/options.rs | 22 ++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/proxmox-api-macro/src/api/method.rs b/proxmox-api-macro/src/api/method.rs index d3646699..f7089941 100644 --- a/proxmox-api-macro/src/api/method.rs +++ b/proxmox-api-macro/src/api/method.rs @@ -1,4 +1,5 @@ use std::convert::{TryFrom, TryInto}; +use std::mem; use failure::Error; @@ -6,6 +7,7 @@ use proc_macro2::{Span, TokenStream}; use quote::{quote, quote_spanned}; use syn::spanned::Spanned; use syn::Ident; +use syn::visit_mut::{self, VisitMut}; use super::{Schema, SchemaItem}; use crate::util::{self, FieldName, JSONObject}; @@ -55,6 +57,9 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result(&'a Schema); + +impl<'a> VisitMut for DefaultParameters<'a> { + fn visit_expr_mut(&mut self, i: &mut syn::Expr) { + if let syn::Expr::Macro(exprmac) = i { + if exprmac.mac.path.is_ident("api_get_default") { + // replace api_get_default macros with the actual default found in the #[api] + // macro. + match self.get_default(mem::take(&mut exprmac.mac.tokens)) { + Ok(expr) => *i = expr, + Err(err) => { + *i = syn::Expr::Verbatim(err.to_compile_error()); + return; + } + } + } + } + + visit_mut::visit_expr_mut(self, i) + } +} + +impl<'a> DefaultParameters<'a> { + fn get_default(&self, param_tokens: TokenStream) -> Result { + let param_name: syn::LitStr = syn::parse2(param_tokens)?; + match self.0.find_obj_property_by_ident(¶m_name.value()) { + Some((_ident, _optional, schema)) => match schema.find_schema_property("default") { + Some(def) => Ok(def.clone()), + None => bail!(param_name => "no default found in schema") + } + None => bail!(param_name => "todo"), + } + } +} diff --git a/proxmox-api-macro/tests/options.rs b/proxmox-api-macro/tests/options.rs index 256326c9..94195c35 100644 --- a/proxmox-api-macro/tests/options.rs +++ b/proxmox-api-macro/tests/options.rs @@ -21,6 +21,24 @@ pub fn test_option(value: bool) -> Result { Ok(value) } +#[api( + input: { + properties: { + value: { + description: "The optional value with default.", + optional: true, + default: 5, + } + } + } +)] +/// Print the given message. +/// +/// Returns: the input. +pub fn test_default_macro(value: Option) -> Result { + Ok(value.unwrap_or(api_get_default!("value"))) +} + struct RpcEnv; impl proxmox::api::RpcEnvironment for RpcEnv { fn set_result_attrib(&mut self, name: &str, value: Value) { @@ -65,4 +83,8 @@ fn test_invocations() { let value = api_function_test_option(json!({"value": false}), &API_METHOD_TEST_OPTION, &mut env) .expect("func with option should work"); assert_eq!(value, false); + + let value = api_function_test_default_macro(json!({}), &API_METHOD_TEST_DEFAULT_MACRO, &mut env) + .expect("func with option should work"); + assert_eq!(value, 5); }