mirror of
https://git.proxmox.com/git/proxmox
synced 2025-08-06 13:14:42 +00:00
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,
|
value: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
//let mut body_type = definition
|
let body_type = definition
|
||||||
// .remove("body")
|
.remove("body")
|
||||||
// .map(|v| v.expect_tt())
|
.map(|v| v.expect_type())
|
||||||
// .transpose()?
|
.transpose()?
|
||||||
// .unwrap_or_else(|| quote! { ::hyper::Body });
|
.map_or_else(
|
||||||
|
|| quote! { ::hyper::Body },
|
||||||
|
|v| v.into_token_stream(),
|
||||||
|
);
|
||||||
|
|
||||||
let vis = std::mem::replace(&mut item.vis, syn::Visibility::Inherited);
|
let vis = std::mem::replace(&mut item.vis, syn::Visibility::Inherited);
|
||||||
let span = item.ident.span();
|
let span = item.ident.span();
|
||||||
@ -173,8 +176,12 @@ fn handle_function(
|
|||||||
// perform the serialization/http::Response-building automatically.
|
// perform the serialization/http::Response-building automatically.
|
||||||
// (Alternatively we could do exactly that with a trait so we don't have to parse the
|
// (Alternatively we could do exactly that with a trait so we don't have to parse the
|
||||||
// return type?)
|
// return type?)
|
||||||
fn wrapped_api_handler(args: ::serde_json::Value) -> ::proxmox::api::ApiFuture {
|
fn wrapped_api_handler(
|
||||||
async fn handler(mut args: ::serde_json::Value) -> ::proxmox::api::ApiOutput {
|
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 mut empty_args = ::serde_json::map::Map::new();
|
||||||
let args = args.as_object_mut()
|
let args = args.as_object_mut()
|
||||||
.unwrap_or(&mut empty_args);
|
.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
|
// 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 :(
|
// `async fn`, since an `async fn` cannot appear as a return type :(
|
||||||
impl ::std::ops::Deref for #struct_name {
|
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 {
|
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)
|
#struct_name::#impl_ident(#passed_args)
|
||||||
};
|
};
|
||||||
&FUNC
|
&FUNC
|
||||||
@ -261,7 +268,7 @@ fn handle_function(
|
|||||||
//
|
//
|
||||||
// Note that technically we don't need the `description` member in this trait, as this is
|
// Note that technically we don't need the `description` member in this trait, as this is
|
||||||
// mostly used at compile time for documentation!
|
// 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 description(&self) -> &'static str {
|
||||||
#fn_api_description
|
#fn_api_description
|
||||||
}
|
}
|
||||||
@ -283,7 +290,7 @@ fn handle_function(
|
|||||||
#fn_api_reload_timezone
|
#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
|
#struct_name::wrapped_api_handler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||||||
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
|
use proc_macro2::{Delimiter, Ident, Span, TokenStream, TokenTree};
|
||||||
|
|
||||||
use failure::{bail, Error};
|
use failure::{bail, Error};
|
||||||
use quote::quote;
|
use quote::{quote, quote_spanned};
|
||||||
use syn::LitStr;
|
use syn::LitStr;
|
||||||
|
|
||||||
use super::parsing::*;
|
use super::parsing::*;
|
||||||
@ -20,11 +20,20 @@ pub fn router_macro(input: TokenStream) -> Result<TokenStream, Error> {
|
|||||||
|
|
||||||
match_keyword(&mut input, "static")?;
|
match_keyword(&mut input, "static")?;
|
||||||
let router_name = need_ident(&mut input)?;
|
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, '=')?;
|
match_punct(&mut input, '=')?;
|
||||||
let content = need_group(&mut input, Delimiter::Brace)?;
|
let content = need_group(&mut input, Delimiter::Brace)?;
|
||||||
|
|
||||||
let router = parse_router(content.stream().into_iter().peekable())?;
|
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);
|
out.extend(router);
|
||||||
|
|
||||||
@ -166,11 +175,11 @@ impl Router {
|
|||||||
Ok(())
|
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;
|
use std::iter::FromIterator;
|
||||||
|
|
||||||
let mut out = quote! {
|
let mut out = quote_spanned! {
|
||||||
::proxmox::api::Router::new()
|
body_type.span() => ::proxmox::api::Router::<#body_type>::new()
|
||||||
};
|
};
|
||||||
|
|
||||||
fn add_method(out: &mut TokenStream, name: &'static str, func_name: Ident) {
|
fn add_method(out: &mut TokenStream, name: &'static str, func_name: Ident) {
|
||||||
@ -196,14 +205,14 @@ impl Router {
|
|||||||
match self.subroute {
|
match self.subroute {
|
||||||
None => (),
|
None => (),
|
||||||
Some(SubRoute::Parameter(name, router)) => {
|
Some(SubRoute::Parameter(name, router)) => {
|
||||||
let router = router.into_token_stream(None);
|
let router = router.into_token_stream(body_type, None);
|
||||||
out.extend(quote! {
|
out.extend(quote! {
|
||||||
.parameter_subdir(#name, #router)
|
.parameter_subdir(#name, #router)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(SubRoute::Directories(hash)) => {
|
Some(SubRoute::Directories(hash)) => {
|
||||||
for (name, router) in 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! {
|
out.extend(quote! {
|
||||||
.subdir(#name, #router)
|
.subdir(#name, #router)
|
||||||
});
|
});
|
||||||
@ -224,12 +233,12 @@ impl Router {
|
|||||||
quote! {
|
quote! {
|
||||||
#[allow(non_camel_case_types)]
|
#[allow(non_camel_case_types)]
|
||||||
struct #type_name(
|
struct #type_name(
|
||||||
std::cell::Cell<Option<::proxmox::api::Router>>,
|
std::cell::Cell<Option<::proxmox::api::Router<#body_type>>>,
|
||||||
std::sync::Once,
|
std::sync::Once,
|
||||||
);
|
);
|
||||||
unsafe impl Sync for #type_name {}
|
unsafe impl Sync for #type_name {}
|
||||||
impl std::ops::Deref 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 {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.1.call_once(|| unsafe {
|
self.1.call_once(|| unsafe {
|
||||||
self.0.set(Some(#router_expression));
|
self.0.set(Some(#router_expression));
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#![feature(async_await)]
|
#![feature(async_await)]
|
||||||
|
|
||||||
|
use bytes::Bytes;
|
||||||
use failure::{bail, Error};
|
use failure::{bail, Error};
|
||||||
use serde_derive::{Deserialize, Serialize};
|
use serde_derive::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
@ -42,6 +43,7 @@ pub struct Person {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[api({
|
#[api({
|
||||||
|
body: Bytes,
|
||||||
description: "A test function returning a fixed text",
|
description: "A test function returning a fixed text",
|
||||||
parameters: {},
|
parameters: {},
|
||||||
})]
|
})]
|
||||||
@ -50,6 +52,7 @@ async fn test_body() -> Result<&'static str, Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[api({
|
#[api({
|
||||||
|
body: Bytes,
|
||||||
description: "Loopback the `input` parameter",
|
description: "Loopback the `input` parameter",
|
||||||
parameters: {
|
parameters: {
|
||||||
input: "the input",
|
input: "the input",
|
||||||
@ -60,20 +63,21 @@ async fn get_loopback(param: String) -> Result<String, Error> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[api({
|
#[api({
|
||||||
|
body: Bytes,
|
||||||
description: "Loopback the `input` parameter",
|
description: "Loopback the `input` parameter",
|
||||||
parameters: {
|
parameters: {
|
||||||
input: "the input",
|
input: "the input",
|
||||||
},
|
},
|
||||||
returns: String
|
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 || {
|
Box::pin((async move || {
|
||||||
proxmox::api::IntoApiOutput::into_api_output(param)
|
proxmox::api::IntoApiOutput::into_api_output(param)
|
||||||
})())
|
})())
|
||||||
}
|
}
|
||||||
|
|
||||||
proxmox_api_macro::router! {
|
proxmox_api_macro::router! {
|
||||||
static TEST_ROUTER = {
|
static TEST_ROUTER: Router<Bytes> = {
|
||||||
GET: test_body,
|
GET: test_body,
|
||||||
|
|
||||||
/subdir: { 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
|
let (router, parameters) = router
|
||||||
.lookup(path)
|
.lookup(path)
|
||||||
.expect("expected method to exist on test router");
|
.expect("expected method to exist on test router");
|
||||||
|
Loading…
Reference in New Issue
Block a user