macro: body type support for router and api macros
Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
This commit is contained in:
parent
10451f65eb
commit
7db28deaff
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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<TokenStream, Error> {
|
||||
|
||||
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<Ident>) -> TokenStream {
|
||||
fn into_token_stream(self, body_type: &Ident, name: Option<Ident>) -> 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<Option<::proxmox::api::Router>>,
|
||||
std::cell::Cell<Option<::proxmox::api::Router<#body_type>>>,
|
||||
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));
|
||||
|
@ -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<String, Error> {
|
||||
}
|
||||
|
||||
#[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<Bytes> {
|
||||
Box::pin((async move || {
|
||||
proxmox::api::IntoApiOutput::into_api_output(param)
|
||||
})())
|
||||
}
|
||||
|
||||
proxmox_api_macro::router! {
|
||||
static TEST_ROUTER = {
|
||||
static TEST_ROUTER: Router<Bytes> = {
|
||||
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<Bytes>, path: &str, expect: &'static str) {
|
||||
let (router, parameters) = router
|
||||
.lookup(path)
|
||||
.expect("expected method to exist on test router");
|
||||
|
Loading…
Reference in New Issue
Block a user