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 8BB9C61D8E for ; Fri, 18 Dec 2020 12:37:44 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 13E352F971 for ; Fri, 18 Dec 2020 12:37:44 +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 198192F82C 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 043B8452FA for ; Fri, 18 Dec 2020 12:26:27 +0100 (CET) From: Wolfgang Bumiller To: pbs-devel@lists.proxmox.com Date: Fri, 18 Dec 2020 12:26:01 +0100 Message-Id: <20201218112608.6845-14-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 0.016 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [method.rs] Subject: [pbs-devel] [PATCH proxmox 13/18] api-macro: factor parameter extraction into a function 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:44 -0000 Signed-off-by: Wolfgang Bumiller --- proxmox-api-macro/src/api/method.rs | 142 +++++++++++++++++----------- 1 file changed, 85 insertions(+), 57 deletions(-) diff --git a/proxmox-api-macro/src/api/method.rs b/proxmox-api-macro/src/api/method.rs index 3d6e9ed..ff5d3e0 100644 --- a/proxmox-api-macro/src/api/method.rs +++ b/proxmox-api-macro/src/api/method.rs @@ -458,63 +458,15 @@ fn create_wrapper_function( ParameterType::ApiMethod => args.extend(quote_spanned! { span => api_method_param, }), ParameterType::RpcEnv => args.extend(quote_spanned! { span => rpc_env_param, }), ParameterType::Normal(param) => { - let name_str = syn::LitStr::new(name.as_str(), span); - let arg_name = - Ident::new(&format!("input_arg_{}", name.as_ident().to_string()), span); - - // Optional parameters are expected to be Option<> types in the real function - // signature, so we can just keep the returned Option from `input_map.remove()`. - body.extend(quote_spanned! { span => - let #arg_name = input_map - .remove(#name_str) - .map(::serde_json::from_value) - .transpose()? - }); - let default_value = param.entry.schema.find_schema_property("default"); - if !param.entry.optional { - // Non-optional types need to be extracted out of the option though (unless - // they have a default): - // - // Whether the parameter is optional should have been verified by the schema - // verifier already, so here we just use anyhow::bail! instead of building a - // proper http error! - body.extend(quote_spanned! { span => - .ok_or_else(|| ::anyhow::format_err!( - "missing non-optional parameter: {}", - #name_str, - ))? - }); - } - let no_option_type = util::is_option_type(param.ty).is_none(); - - if let Some(def) = &default_value { - let name_uc = name.as_ident().to_string().to_uppercase(); - let name = Ident::new( - &format!("API_METHOD_{}_PARAM_DEFAULT_{}", func_uc, name_uc), - span, - ); - // strip possible Option<> from this type: - let ty = util::is_option_type(param.ty).unwrap_or(param.ty); - default_consts.extend(quote_spanned! { span => - pub const #name: #ty = #def; - }); - if param.entry.optional && no_option_type { - // Optional parameter without an Option type requires a default: - body.extend(quote_spanned! { span => - .unwrap_or(#name) - }); - } - } else if param.entry.optional && no_option_type { - // FIXME: we should not be able to reach this without having produced another - // error above already anyway? - error!(param.ty => "Optional parameter without Option requires a default"); - // we produced an error so just write something that will compile - body.extend(quote_spanned! { span => - .unwrap_or_else(|| unreachable!()) - }); - } - body.extend(quote_spanned! { span => ; }); - args.extend(quote_spanned! { span => #arg_name, }); + extract_normal_parameter( + param, + &mut body, + &mut args, + &func_uc, + name, + span, + default_consts, + )?; } } } @@ -573,6 +525,82 @@ fn create_wrapper_function( Ok(api_func_name) } +fn extract_normal_parameter( + param: NormalParameter, + body: &mut TokenStream, + args: &mut TokenStream, + func_uc: &str, + name: FieldName, + name_span: Span, + default_consts: &mut TokenStream, +) -> Result<(), Error> { + let span = name_span; // renamed during refactorization + let name_str = syn::LitStr::new(name.as_str(), span); + let arg_name = Ident::new(&format!("input_arg_{}", name.as_ident().to_string()), span); + + // Optional parameters are expected to be Option<> types in the real function + // signature, so we can just keep the returned Option from `input_map.remove()`. + body.extend(quote_spanned! { span => + let #arg_name = input_map + .remove(#name_str) + .map(::serde_json::from_value) + .transpose()? + }); + + let default_value = param.entry.schema.find_schema_property("default"); + if !param.entry.optional { + // Non-optional types need to be extracted out of the option though (unless + // they have a default): + // + // Whether the parameter is optional should have been verified by the schema + // verifier already, so here we just use anyhow::bail! instead of building a + // proper http error! + body.extend(quote_spanned! { span => + .ok_or_else(|| ::anyhow::format_err!( + "missing non-optional parameter: {}", + #name_str, + ))? + }); + } + + let no_option_type = util::is_option_type(param.ty).is_none(); + + if let Some(def) = &default_value { + let name_uc = name.as_ident().to_string().to_uppercase(); + let name = Ident::new( + &format!("API_METHOD_{}_PARAM_DEFAULT_{}", func_uc, name_uc), + span, + ); + + // strip possible Option<> from this type: + let ty = util::is_option_type(param.ty).unwrap_or(param.ty); + default_consts.extend(quote_spanned! { span => + pub const #name: #ty = #def; + }); + + if param.entry.optional && no_option_type { + // Optional parameter without an Option type requires a default: + body.extend(quote_spanned! { span => + .unwrap_or(#name) + }); + } + } else if param.entry.optional && no_option_type { + // FIXME: we should not be able to reach this without having produced another + // error above already anyway? + error!(param.ty => "Optional parameter without Option requires a default"); + + // we produced an error so just write something that will compile + body.extend(quote_spanned! { span => + .unwrap_or_else(|| unreachable!()) + }); + } + + body.extend(quote_spanned! { span => ; }); + args.extend(quote_spanned! { span => #arg_name, }); + + Ok(()) +} + struct DefaultParameters<'a>(&'a Schema); impl<'a> VisitMut for DefaultParameters<'a> { -- 2.20.1