diff --git a/proxmox-api-macro/src/api/method.rs b/proxmox-api-macro/src/api/method.rs index 11dbaeea..1f4a1bd1 100644 --- a/proxmox-api-macro/src/api/method.rs +++ b/proxmox-api-macro/src/api/method.rs @@ -21,10 +21,12 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result = attribs + .remove("returns") + .map(|ret| ret.into_object("return schema definition")) + .transpose()? + .map(|ret| ret.try_into()) + .transpose()?; let protected: bool = attribs .remove("protected") @@ -50,7 +52,14 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result { + let mut inner = TokenStream::new(); + schema.to_schema(&mut inner)?; + ts.extend(quote! { .returns(#inner) }); + } + None => (), + } ts }; @@ -67,7 +76,7 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result Result, attrs: &mut Vec, ) -> Result<(), Error> { let mut doc_comment = String::new(); @@ -107,7 +116,7 @@ fn api_function_attributes( fn derive_descriptions( input_schema: &mut Schema, - returns_schema: &mut Schema, + returns_schema: &mut Option, doc_comment: &str, doc_span: Span, ) -> Result<(), Error> { @@ -126,16 +135,18 @@ fn derive_descriptions( } if let Some(second) = parts.next() { - if returns_schema.description.is_none() { - returns_schema.description = Some(syn::LitStr::new(second.trim(), doc_span)); + if let Some(ref mut returns_schema) = returns_schema { + 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!" - ); + if parts.next().is_some() { + bail!( + doc_span, + "multiple 'Returns:' sections found in doc comment!" + ); + } } Ok(()) @@ -150,7 +161,7 @@ enum ParameterType<'a> { fn handle_function_signature( input_schema: &mut Schema, - returns_schema: &mut Schema, + returns_schema: &mut Option, func: &mut syn::ItemFn, wrapper_ts: &mut TokenStream, ) -> Result { @@ -304,7 +315,7 @@ fn is_value_type(ty: &syn::Type) -> bool { fn create_wrapper_function( _input_schema: &Schema, - _returns_schema: &Schema, + returns_schema: &Option, param_list: Vec<(SimpleIdent, ParameterType)>, func: &syn::ItemFn, wrapper_ts: &mut TokenStream, @@ -316,6 +327,7 @@ fn create_wrapper_function( let mut body = TokenStream::new(); let mut args = TokenStream::new(); + let mut return_stmt = TokenStream::new(); for (name, param) in param_list { let span = name.span(); @@ -354,6 +366,17 @@ fn create_wrapper_function( } } + if returns_schema.is_some() { + return_stmt.extend(quote! { + Ok(::serde_json::to_value(output)?) + }); + } else { + return_stmt.extend(quote! { + let _ = output; + Ok(::serde_json::Value::Null) + }); + } + // build the wrapping function: let func_name = &func.sig.ident; wrapper_ts.extend(quote! { @@ -364,7 +387,8 @@ fn create_wrapper_function( ) -> Result<::serde_json::Value, ::failure::Error> { if let ::serde_json::Value::Object(ref mut input_map) = &mut input_params { #body - Ok(::serde_json::to_value(#func_name(#args)?)?) + let output = #func_name(#args)?; + #return_stmt } else { ::failure::bail!("api function wrapper called with a non-object json value"); } diff --git a/proxmox-api-macro/tests/api1.rs b/proxmox-api-macro/tests/api1.rs index cb8b4754..cd988855 100644 --- a/proxmox-api-macro/tests/api1.rs +++ b/proxmox-api-macro/tests/api1.rs @@ -84,3 +84,19 @@ pub fn create_ticket_direct(username: String, password: String) -> Result<&'stat // successfully produce a `serde_json::Value`! Ok("an:invalid:ticket") } + +#[api( + input: { + properties: { + verbose: { + type: Boolean, + description: "Verbose output.", + }, + }, + }, +)] +/// Test something +pub fn some_call(verbose: bool) -> Result<(), Error> { + let _ = verbose; + Ok(()) +}