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