From 7db28deaff0c1c97efa976d5f026e62d6b25088d Mon Sep 17 00:00:00 2001 From: Wolfgang Bumiller Date: Mon, 10 Jun 2019 20:05:20 +0200 Subject: [PATCH] macro: body type support for router and api macros Signed-off-by: Wolfgang Bumiller --- proxmox-api-macro/src/api_macro.rs | 29 +++++++++++++++++---------- proxmox-api-macro/src/router_macro.rs | 27 ++++++++++++++++--------- proxmox-api-macro/tests/basic.rs | 10 ++++++--- 3 files changed, 43 insertions(+), 23 deletions(-) diff --git a/proxmox-api-macro/src/api_macro.rs b/proxmox-api-macro/src/api_macro.rs index b3a6ff4e..d93264f9 100644 --- a/proxmox-api-macro/src/api_macro.rs +++ b/proxmox-api-macro/src/api_macro.rs @@ -74,11 +74,14 @@ fn handle_function( value: false, }); - //let mut body_type = definition - // .remove("body") - // .map(|v| v.expect_tt()) - // .transpose()? - // .unwrap_or_else(|| quote! { ::hyper::Body }); + let body_type = definition + .remove("body") + .map(|v| v.expect_type()) + .transpose()? + .map_or_else( + || quote! { ::hyper::Body }, + |v| v.into_token_stream(), + ); let vis = std::mem::replace(&mut item.vis, syn::Visibility::Inherited); let span = item.ident.span(); @@ -173,8 +176,12 @@ fn handle_function( // perform the serialization/http::Response-building automatically. // (Alternatively we could do exactly that with a trait so we don't have to parse the // return type?) - fn wrapped_api_handler(args: ::serde_json::Value) -> ::proxmox::api::ApiFuture { - async fn handler(mut args: ::serde_json::Value) -> ::proxmox::api::ApiOutput { + fn wrapped_api_handler( + args: ::serde_json::Value, + ) -> ::proxmox::api::ApiFuture<#body_type> { + async fn handler( + mut args: ::serde_json::Value, + ) -> ::proxmox::api::ApiOutput<#body_type> { let mut empty_args = ::serde_json::map::Map::new(); let args = args.as_object_mut() .unwrap_or(&mut empty_args); @@ -243,10 +250,10 @@ fn handle_function( // Unfortunately we cannot return the actual function since that won't work for // `async fn`, since an `async fn` cannot appear as a return type :( impl ::std::ops::Deref for #struct_name { - type Target = fn(#inputs) -> ::proxmox::api::ApiFuture; + type Target = fn(#inputs) -> ::proxmox::api::ApiFuture<#body_type>; fn deref(&self) -> &Self::Target { - const FUNC: fn(#inputs) -> ::proxmox::api::ApiFuture = |#inputs| { + const FUNC: fn(#inputs) -> ::proxmox::api::ApiFuture<#body_type> = |#inputs| { #struct_name::#impl_ident(#passed_args) }; &FUNC @@ -261,7 +268,7 @@ fn handle_function( // // Note that technically we don't need the `description` member in this trait, as this is // mostly used at compile time for documentation! - impl ::proxmox::api::ApiMethodInfo for #struct_name { + impl ::proxmox::api::ApiMethodInfo<#body_type> for #struct_name { fn description(&self) -> &'static str { #fn_api_description } @@ -283,7 +290,7 @@ fn handle_function( #fn_api_reload_timezone } - fn handler(&self) -> fn(::serde_json::Value) -> ::proxmox::api::ApiFuture { + fn handler(&self) -> fn(::serde_json::Value) -> ::proxmox::api::ApiFuture<#body_type> { #struct_name::wrapped_api_handler } } diff --git a/proxmox-api-macro/src/router_macro.rs b/proxmox-api-macro/src/router_macro.rs index fd7bcd63..f7e388b2 100644 --- a/proxmox-api-macro/src/router_macro.rs +++ b/proxmox-api-macro/src/router_macro.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree}; use failure::{bail, Error}; -use quote::quote; +use quote::{quote, quote_spanned}; use syn::LitStr; use super::parsing::*; @@ -20,11 +20,20 @@ pub fn router_macro(input: TokenStream) -> Result { match_keyword(&mut input, "static")?; let router_name = need_ident(&mut input)?; + + match_colon(&mut input)?; + match_keyword(&mut input, "Router"); + match_punct(&mut input, '<')?; + let body_type = need_ident(&mut input)?; + match_punct(&mut input, '>')?; + match_punct(&mut input, '=')?; let content = need_group(&mut input, Delimiter::Brace)?; let router = parse_router(content.stream().into_iter().peekable())?; - let router = router.into_token_stream(Some(router_name)); + let router = router.into_token_stream(&body_type, Some(router_name)); + + //eprintln!("{}", router.to_string()); out.extend(router); @@ -166,11 +175,11 @@ impl Router { Ok(()) } - fn into_token_stream(self, name: Option) -> TokenStream { + fn into_token_stream(self, body_type: &Ident, name: Option) -> TokenStream { use std::iter::FromIterator; - let mut out = quote! { - ::proxmox::api::Router::new() + let mut out = quote_spanned! { + body_type.span() => ::proxmox::api::Router::<#body_type>::new() }; fn add_method(out: &mut TokenStream, name: &'static str, func_name: Ident) { @@ -196,14 +205,14 @@ impl Router { match self.subroute { None => (), Some(SubRoute::Parameter(name, router)) => { - let router = router.into_token_stream(None); + let router = router.into_token_stream(body_type, None); out.extend(quote! { .parameter_subdir(#name, #router) }); } Some(SubRoute::Directories(hash)) => { for (name, router) in hash { - let router = router.into_token_stream(None); + let router = router.into_token_stream(body_type, None); out.extend(quote! { .subdir(#name, #router) }); @@ -224,12 +233,12 @@ impl Router { quote! { #[allow(non_camel_case_types)] struct #type_name( - std::cell::Cell>, + std::cell::Cell>>, std::sync::Once, ); unsafe impl Sync for #type_name {} impl std::ops::Deref for #type_name { - type Target = ::proxmox::api::Router; + type Target = ::proxmox::api::Router<#body_type>; fn deref(&self) -> &Self::Target { self.1.call_once(|| unsafe { self.0.set(Some(#router_expression)); diff --git a/proxmox-api-macro/tests/basic.rs b/proxmox-api-macro/tests/basic.rs index 1ebce5e7..d1af1ae0 100644 --- a/proxmox-api-macro/tests/basic.rs +++ b/proxmox-api-macro/tests/basic.rs @@ -1,5 +1,6 @@ #![feature(async_await)] +use bytes::Bytes; use failure::{bail, Error}; use serde_derive::{Deserialize, Serialize}; use serde_json::Value; @@ -42,6 +43,7 @@ pub struct Person { } #[api({ + body: Bytes, description: "A test function returning a fixed text", parameters: {}, })] @@ -50,6 +52,7 @@ async fn test_body() -> Result<&'static str, Error> { } #[api({ + body: Bytes, description: "Loopback the `input` parameter", parameters: { input: "the input", @@ -60,20 +63,21 @@ async fn get_loopback(param: String) -> Result { } #[api({ + body: Bytes, description: "Loopback the `input` parameter", parameters: { input: "the input", }, returns: String })] -fn non_async_test(param: String) -> proxmox::api::ApiFuture { +fn non_async_test(param: String) -> proxmox::api::ApiFuture { Box::pin((async move || { proxmox::api::IntoApiOutput::into_api_output(param) })()) } proxmox_api_macro::router! { - static TEST_ROUTER = { + static TEST_ROUTER: Router = { GET: test_body, /subdir: { GET: test_body }, @@ -95,7 +99,7 @@ proxmox_api_macro::router! { }; } -fn check_body(router: &Router, path: &str, expect: &'static str) { +fn check_body(router: &Router, path: &str, expect: &'static str) { let (router, parameters) = router .lookup(path) .expect("expected method to exist on test router");