proxmox-api-macro: add 'streaming' option

to generate the `Streaming` variants of the ApiHandler

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
Dominik Csapak 2022-04-08 11:56:03 +02:00 committed by Wolfgang Bumiller
parent f585722aad
commit ca3b25869c
2 changed files with 105 additions and 40 deletions

View File

@ -169,6 +169,12 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<T
.transpose()? .transpose()?
.unwrap_or(false); .unwrap_or(false);
let streaming: bool = attribs
.remove("streaming")
.map(TryFrom::try_from)
.transpose()?
.unwrap_or(false);
if !attribs.is_empty() { if !attribs.is_empty() {
error!( error!(
attribs.span(), attribs.span(),
@ -195,6 +201,7 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<T
&mut func, &mut func,
&mut wrapper_ts, &mut wrapper_ts,
&mut default_consts, &mut default_consts,
streaming,
)?; )?;
// input schema is done, let's give the method body a chance to extract default parameters: // input schema is done, let's give the method body a chance to extract default parameters:
@ -217,10 +224,11 @@ pub fn handle_method(mut attribs: JSONObject, mut func: syn::ItemFn) -> Result<T
returns_schema_setter = quote! { .returns(#inner) }; returns_schema_setter = quote! { .returns(#inner) };
} }
let api_handler = if is_async { let api_handler = match (streaming, is_async) {
quote! { ::proxmox_router::ApiHandler::Async(&#api_func_name) } (true, true) => quote! { ::proxmox_router::ApiHandler::StreamingAsync(&#api_func_name) },
} else { (true, false) => quote! { ::proxmox_router::ApiHandler::StreamingSync(&#api_func_name) },
quote! { ::proxmox_router::ApiHandler::Sync(&#api_func_name) } (false, true) => quote! { ::proxmox_router::ApiHandler::Async(&#api_func_name) },
(false, false) => quote! { ::proxmox_router::ApiHandler::Sync(&#api_func_name) },
}; };
Ok(quote_spanned! { func.sig.span() => Ok(quote_spanned! { func.sig.span() =>
@ -279,6 +287,7 @@ fn handle_function_signature(
func: &mut syn::ItemFn, func: &mut syn::ItemFn,
wrapper_ts: &mut TokenStream, wrapper_ts: &mut TokenStream,
default_consts: &mut TokenStream, default_consts: &mut TokenStream,
streaming: bool,
) -> Result<Ident, Error> { ) -> Result<Ident, Error> {
let sig = &func.sig; let sig = &func.sig;
let is_async = sig.asyncness.is_some(); let is_async = sig.asyncness.is_some();
@ -414,6 +423,7 @@ fn handle_function_signature(
wrapper_ts, wrapper_ts,
default_consts, default_consts,
is_async, is_async,
streaming,
) )
} }
@ -471,6 +481,7 @@ fn create_wrapper_function(
wrapper_ts: &mut TokenStream, wrapper_ts: &mut TokenStream,
default_consts: &mut TokenStream, default_consts: &mut TokenStream,
is_async: bool, is_async: bool,
streaming: bool,
) -> Result<Ident, Error> { ) -> Result<Ident, Error> {
let api_func_name = Ident::new( let api_func_name = Ident::new(
&format!("api_function_{}", &func.sig.ident), &format!("api_function_{}", &func.sig.ident),
@ -512,45 +523,83 @@ fn create_wrapper_function(
_ => Some(quote!(?)), _ => Some(quote!(?)),
}; };
let body = quote! { let body = if streaming {
if let ::serde_json::Value::Object(ref mut input_map) = &mut input_params { quote! {
#body if let ::serde_json::Value::Object(ref mut input_map) = &mut input_params {
Ok(::serde_json::to_value(#func_name(#args) #await_keyword #question_mark)?) #body
} else { let res = #func_name(#args) #await_keyword #question_mark;
::anyhow::bail!("api function wrapper called with a non-object json value"); let res: ::std::boxed::Box<dyn ::proxmox_router::SerializableReturn + Send> = ::std::boxed::Box::new(res);
Ok(res)
} else {
::anyhow::bail!("api function wrapper called with a non-object json value");
}
}
} else {
quote! {
if let ::serde_json::Value::Object(ref mut input_map) = &mut input_params {
#body
Ok(::serde_json::to_value(#func_name(#args) #await_keyword #question_mark)?)
} else {
::anyhow::bail!("api function wrapper called with a non-object json value");
}
} }
}; };
if is_async { match (streaming, is_async) {
wrapper_ts.extend(quote! { (true, true) => {
fn #api_func_name<'a>( wrapper_ts.extend(quote! {
mut input_params: ::serde_json::Value, fn #api_func_name<'a>(
api_method_param: &'static ::proxmox_router::ApiMethod, mut input_params: ::serde_json::Value,
rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment, api_method_param: &'static ::proxmox_router::ApiMethod,
) -> ::proxmox_router::ApiFuture<'a> { rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment,
//async fn func<'a>( ) -> ::proxmox_router::StreamingApiFuture<'a> {
// mut input_params: ::serde_json::Value, ::std::boxed::Box::pin(async move { #body })
// api_method_param: &'static ::proxmox_router::ApiMethod, }
// rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment, });
//) -> ::std::result::Result<::serde_json::Value, ::anyhow::Error> { }
// #body (true, false) => {
//} wrapper_ts.extend(quote! {
//::std::boxed::Box::pin(async move { fn #api_func_name(
// func(input_params, api_method_param, rpc_env_param).await mut input_params: ::serde_json::Value,
//}) api_method_param: &::proxmox_router::ApiMethod,
::std::boxed::Box::pin(async move { #body }) rpc_env_param: &mut dyn ::proxmox_router::RpcEnvironment,
} ) -> ::std::result::Result<::std::boxed::Box<dyn ::proxmox_router::SerializableReturn + Send>, ::anyhow::Error> {
}); #body
} else { }
wrapper_ts.extend(quote! { });
fn #api_func_name( }
mut input_params: ::serde_json::Value, (false, true) => {
api_method_param: &::proxmox_router::ApiMethod, wrapper_ts.extend(quote! {
rpc_env_param: &mut dyn ::proxmox_router::RpcEnvironment, fn #api_func_name<'a>(
) -> ::std::result::Result<::serde_json::Value, ::anyhow::Error> { mut input_params: ::serde_json::Value,
#body api_method_param: &'static ::proxmox_router::ApiMethod,
} rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment,
}); ) -> ::proxmox_router::ApiFuture<'a> {
//async fn func<'a>(
// mut input_params: ::serde_json::Value,
// api_method_param: &'static ::proxmox_router::ApiMethod,
// rpc_env_param: &'a mut dyn ::proxmox_router::RpcEnvironment,
//) -> ::std::result::Result<::serde_json::Value, ::anyhow::Error> {
// #body
//}
//::std::boxed::Box::pin(async move {
// func(input_params, api_method_param, rpc_env_param).await
//})
::std::boxed::Box::pin(async move { #body })
}
});
}
(false, false) => {
wrapper_ts.extend(quote! {
fn #api_func_name(
mut input_params: ::serde_json::Value,
api_method_param: &::proxmox_router::ApiMethod,
rpc_env_param: &mut dyn ::proxmox_router::RpcEnvironment,
) -> ::std::result::Result<::serde_json::Value, ::anyhow::Error> {
#body
}
});
}
} }
Ok(api_func_name) Ok(api_func_name)

View File

@ -235,6 +235,22 @@ pub fn basic_function() -> Result<(), Error> {
Ok(()) Ok(())
} }
#[api(
streaming: true,
)]
/// streaming async call
pub async fn streaming_async_call() -> Result<(), Error> {
Ok(())
}
#[api(
streaming: true,
)]
/// streaming sync call
pub fn streaming_sync_call() -> Result<(), Error> {
Ok(())
}
#[api( #[api(
input: { input: {
properties: { properties: {