hooks_derive_core/
detected.rs

1use proc_macro2::{Span, TokenStream};
2use quote::{quote, quote_spanned, ToTokens};
3
4pub struct DetectedHooksTokens {
5    pub data_expr: TokenStream,
6    pub fn_arg_data_pat: TokenStream,
7    pub fn_stmts_extract_data: Option<TokenStream>,
8}
9
10pub struct DetectedHookCall {
11    pub ident: syn::Ident,
12    pub expr_call: syn::ExprCall,
13}
14
15/// `ty_no_data` defaults to `_`
16pub fn detected_hooks_to_tokens(
17    mut used_hooks: Vec<DetectedHookCall>,
18    hooks_core_path: impl ToTokens,
19    expr_no_data: TokenStream,
20    ty_no_data: Option<TokenStream>,
21    span: Span,
22) -> DetectedHooksTokens {
23    match used_hooks.len() {
24        0 => DetectedHooksTokens {
25            data_expr: expr_no_data,
26            fn_arg_data_pat: {
27                let ty_no_data = ty_no_data.unwrap_or_else(|| quote!(_));
28                quote! {_: ::core::pin::Pin<&mut #ty_no_data>}
29            },
30            fn_stmts_extract_data: None,
31        },
32        1 => {
33            let h = used_hooks.pop().unwrap();
34            DetectedHooksTokens {
35                data_expr: h.expr_call.into_token_stream(),
36                fn_arg_data_pat: h.ident.into_token_stream(),
37                fn_stmts_extract_data: None,
38            }
39        }
40        _ => {
41            let expr_hooks_data = {
42                let mut used_hooks = used_hooks.iter().map(|h| &h.expr_call);
43                let first = used_hooks.next().unwrap();
44                let second = used_hooks.next().unwrap();
45
46                quote_spanned! { span =>
47                    #hooks_core_path ::hook_pair::HookPair::new(#first , #second)
48                        #( .chain( #used_hooks ) )*
49                }
50            };
51
52            let ident_hooks_data = syn::Ident::new("__hooks_data", span);
53
54            let impl_extract_hooks_data = {
55                let mut stmts = Vec::with_capacity(used_hooks.len());
56
57                while let Some(used_hook) = used_hooks.pop() {
58                    let used_hook_ident = used_hook.ident;
59                    let stmt = if !used_hooks.is_empty() {
60                        quote_spanned! { span =>
61                            let (#ident_hooks_data, #used_hook_ident) = #ident_hooks_data.pin_project();
62                        }
63                    } else {
64                        // This is the first element
65                        quote_spanned! { span =>
66                            let #used_hook_ident = #ident_hooks_data;
67                        }
68                    };
69                    stmts.push(stmt);
70                }
71
72                proc_macro2::TokenStream::from_iter(stmts)
73            };
74
75            DetectedHooksTokens {
76                data_expr: expr_hooks_data,
77                fn_arg_data_pat: ident_hooks_data.into_token_stream(),
78                fn_stmts_extract_data: Some(impl_extract_hooks_data),
79            }
80        }
81    }
82}