From 7799deb0954a5ba2182638d6fe1e3dcf835b23ef Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Tue, 26 Nov 2019 14:18:25 +0100 Subject: [PATCH] api-macro: refactoring: split api macro function Signed-off-by: Wolfgang Bumiller --- proxmox-api-macro/src/api.rs | 168 ++++++++++++++++++++--------------- 1 file changed, 97 insertions(+), 71 deletions(-) diff --git a/proxmox-api-macro/src/api.rs b/proxmox-api-macro/src/api.rs index b9ad8fab..36a4400d 100644 --- a/proxmox-api-macro/src/api.rs +++ b/proxmox-api-macro/src/api.rs @@ -278,76 +278,8 @@ impl Parse for BareAssignment { pub(crate) fn api(_attr: TokenStream, item: TokenStream) -> Result { let mut func: syn::ItemFn = syn::parse2(item)?; - let sig_span = func.sig.span(); - - let mut protected = false; - - let mut input_schema = None; - let mut returns_schema = None; - let mut doc_comment = String::new(); - let doc_span = Span::call_site(); // FIXME: set to first doc comment - for attr in mem::replace(&mut func.attrs, Vec::new()) { - // don't mess with #![...] - if let syn::AttrStyle::Inner(_) = &attr.style { - func.attrs.push(attr); - continue; - } - - if attr.path.is_ident("doc") { - let doc: BareAssignment = syn::parse2(attr.tokens.clone())?; - if !doc_comment.is_empty() { - doc_comment.push_str("\n"); - } - doc_comment.push_str(doc.content.value().trim()); - func.attrs.push(attr); - } else if attr.path.is_ident("input") { - let input: Parenthesized = syn::parse2(attr.tokens)?; - input_schema = Some(input.content); - } else if attr.path.is_ident("returns") { - let input: Parenthesized = syn::parse2(attr.tokens)?; - returns_schema = Some(input.content); - } else if attr.path.is_ident("protected") { - if attr.tokens.is_empty() { - protected = true; - } else { - let value: Parenthesized = syn::parse2(attr.tokens)?; - protected = value.content.value; - } - } else { - func.attrs.push(attr); - } - } - - let mut input_schema = - input_schema.ok_or_else(|| format_err!(sig_span, "missing input schema"))?; - - let mut returns_schema = - returns_schema.ok_or_else(|| format_err!(sig_span, "missing returns schema"))?; - - // If we have a doc comment, allow automatically inferring the description for the input and - // output objects: - if !doc_comment.is_empty() { - let mut parts = doc_comment.split("\nReturns:"); - - if let Some(first) = parts.next() { - if input_schema.description.is_none() { - input_schema.description = Some(syn::LitStr::new(first.trim(), doc_span)); - } - } - - if let Some(second) = parts.next() { - if returns_schema.description.is_none() { - returns_schema.description = Some(syn::LitStr::new(second.trim(), doc_span)); - } - } - - if parts.next().is_some() { - bail!( - doc_span, - "multiple 'Returns:' sections found in doc comment!" - ); - } - } + let (input_schema, returns_schema, protected) = + api_function_attributes(&mut func.attrs, func.sig.span())?; let input_schema = { let mut ts = TokenStream::new(); @@ -368,7 +300,7 @@ pub(crate) fn api(_attr: TokenStream, item: TokenStream) -> Result + Ok(quote_spanned! { func.sig.span() => #vis const #api_method_name: ::proxmox::api::ApiMethod = ::proxmox::api::ApiMethod::new( &::proxmox::api::ApiHandler::Sync(&#func_name), @@ -380,3 +312,97 @@ pub(crate) fn api(_attr: TokenStream, item: TokenStream) -> Result, + sig_span: Span, +) -> Result<(Schema, Schema, bool), Error> { + let mut protected = false; + let mut input_schema = None; + let mut returns_schema = None; + let mut doc_comment = String::new(); + let doc_span = Span::call_site(); // FIXME: set to first doc comment + + for attr in mem::replace(attrs, Vec::new()) { + // don't mess with #![...] + if let syn::AttrStyle::Inner(_) = &attr.style { + attrs.push(attr); + continue; + } + + if attr.path.is_ident("doc") { + let doc: BareAssignment = syn::parse2(attr.tokens.clone())?; + if !doc_comment.is_empty() { + doc_comment.push_str("\n"); + } + doc_comment.push_str(doc.content.value().trim()); + attrs.push(attr); + } else if attr.path.is_ident("input") { + let input: Parenthesized = syn::parse2(attr.tokens)?; + input_schema = Some(input.content); + } else if attr.path.is_ident("returns") { + let input: Parenthesized = syn::parse2(attr.tokens)?; + returns_schema = Some(input.content); + } else if attr.path.is_ident("protected") { + if attr.tokens.is_empty() { + protected = true; + } else { + let value: Parenthesized = syn::parse2(attr.tokens)?; + protected = value.content.value; + } + } else { + attrs.push(attr); + } + } + + let mut input_schema = + input_schema.ok_or_else(|| format_err!(sig_span, "missing input schema"))?; + + let mut returns_schema = + returns_schema.ok_or_else(|| format_err!(sig_span, "missing returns schema"))?; + + derive_descriptions( + &mut input_schema, + &mut returns_schema, + &doc_comment, + doc_span, + )?; + + Ok((input_schema, returns_schema, protected)) +} + +fn derive_descriptions( + input_schema: &mut Schema, + returns_schema: &mut Schema, + doc_comment: &str, + doc_span: Span, +) -> Result<(), Error> { + // If we have a doc comment, allow automatically inferring the description for the input and + // output objects: + if doc_comment.is_empty() { + return Ok(()); + } + + let mut parts = doc_comment.split("\nReturns:"); + + if let Some(first) = parts.next() { + if input_schema.description.is_none() { + input_schema.description = Some(syn::LitStr::new(first.trim(), doc_span)); + } + } + + if let Some(second) = parts.next() { + if returns_schema.description.is_none() { + returns_schema.description = Some(syn::LitStr::new(second.trim(), doc_span)); + } + } + + if parts.next().is_some() { + bail!( + doc_span, + "multiple 'Returns:' sections found in doc comment!" + ); + } + + Ok(()) +}