hooks_derive_core/
detect.rs1use quote::ToTokens;
2use syn::spanned::Spanned;
3
4use crate::{
5 tlpc::{tlpc, ExprCallPath},
6 DetectedHookCall,
7};
8
9pub fn expr_path_is_hook(path: &syn::ExprPath) -> bool {
10 if let Some(last) = path.path.segments.last() {
11 last.ident.to_string().starts_with("use_")
12 } else {
13 false
14 }
15}
16
17pub fn mut_expr_of_statement(stmt: &mut syn::Stmt) -> Option<&mut syn::Expr> {
18 match stmt {
19 syn::Stmt::Local(local) => {
20 if let Some((_, expr)) = &mut local.init {
21 Some(&mut *expr)
22 } else {
23 None
24 }
25 }
26 syn::Stmt::Item(_) => {
27 None
29 }
30 syn::Stmt::Expr(expr) => Some(expr),
31 syn::Stmt::Semi(expr, _) => Some(expr),
32 }
33}
34
35pub fn transform_path_call(
36 e: ExprCallPath,
37 hooks_core_path: impl ToTokens,
38 gen_ident: impl FnOnce(&syn::ExprPath) -> syn::Ident,
39) -> syn::ExprCall {
40 let paren_token = *e.paren_token;
41
42 let path_use_hook: syn::ExprPath = syn::parse_quote! {
44 #hooks_core_path ::Hook::<_>::use_hook
45 };
46 let func_path = std::mem::replace(e.func_path, path_use_hook);
47
48 let ident = gen_ident(&func_path);
49
50 let ident = syn::ExprPath {
51 attrs: vec![],
52 qself: None,
53 path: ident.into(),
54 };
55
56 let hook_args = std::mem::take(e.args);
57 e.args.extend([
59 syn::Expr::Path(ident),
60 syn::Expr::Tuple(syn::ExprTuple {
61 attrs: vec![],
62 paren_token,
63 elems: hook_args,
64 }),
65 ]);
66
67 syn::ExprCall {
68 attrs: vec![],
69 func: Box::new(syn::Expr::Path(func_path)),
70 paren_token,
71 args: Default::default(),
72 }
73}
74
75pub fn detect_hooks<'a>(
76 stmts: impl Iterator<Item = &'a mut syn::Stmt>,
77 hooks_core_path: &impl ToTokens,
78) -> Vec<crate::DetectedHookCall> {
79 let mut used_hooks = vec![];
80
81 let mut mutate_func_path = |e: ExprCallPath| {
82 if expr_path_is_hook(e.func_path) {
83 let mut hook_ident = None;
84 let expr_call = transform_path_call(e, hooks_core_path, |func_path| {
85 let idx = used_hooks.len();
86 let ident = syn::Ident::new(&format!("__hooks_hook_{idx}"), func_path.span());
87 hook_ident = Some(ident.clone());
88 ident
89 });
90 let ident = hook_ident.unwrap();
91 used_hooks.push(DetectedHookCall { ident, expr_call })
92 }
93 };
94
95 for stmt in stmts {
96 if let Some(expr) = mut_expr_of_statement(stmt) {
97 tlpc(expr, &mut mutate_func_path);
98 }
99 }
100
101 used_hooks
102}