1use proc_macro2::TokenStream;
4use quote::quote;
5use syn::{
6 AssocType, GenericArgument, PathArguments, ReturnType, TraitBound, Type, TypeParamBound,
7 TypePath, TypeTraitObject,
8};
9
10const PIN: &str = "Pin";
11const BOX: &str = "Box";
12const FUTURE: &str = "Future";
13const RESULT: &str = "Result";
14
15#[derive(Debug, Clone)]
17pub struct PinBoxFutRet {
18 is_pin_box_fut: bool,
19 is_fut_ret_result: bool,
20 ret_ty: TokenStream,
21}
22
23impl Default for PinBoxFutRet {
24 fn default() -> Self {
25 PinBoxFutRet {
26 is_pin_box_fut: false,
27 is_fut_ret_result: false,
28 ret_ty: quote! {},
29 }
30 }
31}
32
33impl PinBoxFutRet {
34 pub fn parse(ret_ty: &ReturnType) -> PinBoxFutRet {
36 let expect_ty = match ret_ty {
37 ReturnType::Type(_, ty) => ty,
38 _ => return PinBoxFutRet::default(),
39 };
40
41 let expect_pin = match *(expect_ty.clone()) {
42 Type::Path(TypePath { qself: _, path }) => {
43 let last_seg = path.segments.last().cloned();
44 match last_seg.map(|ls| (ls.ident.clone(), ls)) {
45 Some((ls_ident, ls)) if ls_ident == PIN => ls,
46 _ => return PinBoxFutRet::default(),
47 }
48 }
49 _ => return PinBoxFutRet::default(),
50 };
51
52 let expect_box = match &expect_pin.arguments {
53 PathArguments::AngleBracketed(wrapper) => match wrapper.args.last() {
54 Some(GenericArgument::Type(Type::Path(TypePath { qself: _, path }))) => {
55 match path.segments.last().map(|ls| (ls.ident.clone(), ls)) {
56 Some((ls_ident, ls)) if ls_ident == BOX => ls,
57 _ => return PinBoxFutRet::default(),
58 }
59 }
60 _ => return PinBoxFutRet::default(),
61 },
62 _ => return PinBoxFutRet::default(),
63 };
64
65 match &expect_box.arguments {
66 PathArguments::AngleBracketed(wrapper) => match wrapper.args.last() {
67 Some(GenericArgument::Type(Type::TraitObject(TypeTraitObject {
68 dyn_token: _,
69 bounds,
70 }))) => {
71 let mut fut_ret = PinBoxFutRet::default();
72
73 for bound in bounds.iter() {
74 if let TypeParamBound::Trait(TraitBound { path, .. }) = bound {
75 if let Some(arg) = path.segments.last() {
76 if arg.ident == FUTURE {
77 fut_ret.is_pin_box_fut = true;
78 fut_ret.is_fut_ret_result =
79 is_fut_ret_result(&arg.arguments, &mut fut_ret);
80 break;
81 }
82 }
83 }
84 }
85 fut_ret
86 }
87 _ => PinBoxFutRet::default(),
88 },
89 _ => PinBoxFutRet::default(),
90 }
91 }
92
93 pub fn is_ret_pin_box_fut(&self) -> bool {
96 self.is_pin_box_fut
97 }
98
99 pub fn is_fut_ret_result(&self) -> bool {
103 self.is_fut_ret_result
104 }
105
106 pub fn return_type(&self) -> TokenStream {
108 self.ret_ty.clone()
109 }
110}
111
112fn is_fut_ret_result(input: &PathArguments, fut_ret: &mut PinBoxFutRet) -> bool {
113 match input {
114 PathArguments::AngleBracketed(angle_arg) => {
115 match angle_arg.args.first().expect("future output") {
116 GenericArgument::AssocType(AssocType { ty, .. }) => {
117 fut_ret.ret_ty = quote! { #ty };
118
119 match ty {
120 Type::Path(TypePath { path, .. }) => path
121 .segments
122 .last()
123 .unwrap()
124 .ident
125 .to_string()
126 .contains(RESULT),
127 _ => false,
128 }
129 }
130 _ => false,
131 }
132 }
133 _ => false,
134 }
135}