use resource::Arg;
use syn;
use proc_macro2::TokenStream;
use std::fmt;
pub(crate) struct Signature {
ident: syn::Ident,
ret: syn::Type,
box_ret: bool,
args: Vec<Arg>,
is_async: bool,
}
impl Signature {
pub fn new(ident: syn::Ident, ret: syn::Type, args: Vec<Arg>, is_async: bool) -> Signature {
let (ret, box_ret) = if is_async {
let tokens = quote! { Box<Future<Item = #ret, Error = __tw::Error> + Send> };
let ret = syn::parse2(tokens).unwrap();
(ret, true)
} else {
match ret {
syn::Type::ImplTrait(obj) => {
(box_impl_trait(obj), true)
}
ret => (ret, false),
}
};
Signature {
ident,
ret,
box_ret,
args,
is_async,
}
}
pub fn ident(&self) -> &syn::Ident {
&self.ident
}
pub fn args(&self) -> &[Arg] {
&self.args[..]
}
pub fn ret(&self) -> &syn::Type {
&self.ret
}
pub fn is_async(&self) -> bool {
self.is_async
}
pub fn future_ty(&self) -> TokenStream {
let ty = self.ret();
if self.box_ret {
quote! { #ty }
} else {
quote! {
__tw::error::Map<<#ty as __tw::codegen::futures::IntoFuture>::Future>
}
}
}
pub fn dispatch<I>(&self, inner: TokenStream, args: I) -> TokenStream
where I: Iterator<Item = TokenStream>,
{
let ident = self.ident();
if self.is_async {
let ty = self.ret();
quote! {
let inner = #inner.clone();
let ret: #ty = __tw::codegen::async_await::async_to_box_future_send(async_move_hax! {
await!(inner.handler.#ident(#(#args),*))
});
ret
}
} else {
let box_ret = if self.box_ret {
let ty = self.ret();
quote! { let ret: #ty = Box::new(ret); }
} else {
quote!()
};
quote! {
let ret = __tw::error::Map::new(
__tw::codegen::futures::IntoFuture::into_future(#inner.handler.#ident(#(#args),*)));
#box_ret
ret
}
}
}
}
impl fmt::Debug for Signature {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
use quote::ToTokens;
let ret = self.ret.clone().into_token_stream().to_string();
fmt.debug_struct("Signature")
.field("ident", &self.ident.to_string())
.field("ret", &ret)
.field("args", &self.args)
.finish()
}
}
fn box_impl_trait(obj: syn::TypeImplTrait) -> syn::Type {
use syn::TypeParamBound::Trait;
use syn::PathArguments::AngleBracketed;
use syn::GenericArgument::Binding;
let mut item = None;
for bound in &obj.bounds {
let bound = match bound {
Trait(ref bound) => bound,
_ => continue,
};
let segments = &bound.path.segments;
let len = segments.len();
assert!(len > 0);
if segments[len - 1].ident == "Future" {
let args = match segments[len - 1].arguments {
AngleBracketed(ref args) => args,
_ => continue,
};
for arg in &args.args {
let arg = match arg {
Binding(ref arg) => arg,
_ => continue,
};
if arg.ident == "Item" {
item = Some(arg.ty.clone());
break;
}
}
}
}
let item = item.expect("failed to identify `impl T` as `Future`");
syn::parse2(quote! {
Box<Future<Item = #item, Error = __tw::Error> + Send>
}).unwrap()
}