typhoon_metadata_extractor/
parsing.rs1use syn::{Expr, File, Ident, Item, ItemFn, Stmt, Type};
2
3#[derive(Default, Debug)]
4pub struct ParsingContext<'a> {
5 pub contexts: Vec<&'a Ident>,
6 pub instructions: Vec<&'a Ident>,
7 pub accounts: Vec<&'a Ident>,
8}
9
10impl<'a> From<&'a File> for ParsingContext<'a> {
11 fn from(value: &'a File) -> Self {
12 let mut context = ParsingContext::default();
13 value.items.iter().for_each(|item| match item {
14 Item::Impl(item_impl) => {
15 if let Some(ident) = extract_ident(item_impl, "HandlerContext") {
16 context.contexts.push(ident);
17 }
18
19 if let Some(ident) = extract_ident(item_impl, "Owner") {
20 context.accounts.push(ident);
21 }
22 }
23 Item::Fn(item_fn) => {
24 if let Some(instructions) = extract_instruction_idents(item_fn) {
25 context.instructions = instructions;
26 }
27 }
28 _ => (),
29 });
30 context
31 }
32}
33
34fn extract_ident<'a>(item_impl: &'a syn::ItemImpl, trait_name: &str) -> Option<&'a Ident> {
35 let trait_ = item_impl.trait_.as_ref()?;
36 let segment = trait_.1.segments.last()?;
37
38 if segment.ident != trait_name {
39 return None;
40 }
41
42 match *item_impl.self_ty {
43 Type::Path(ref type_path) => Some(&type_path.path.segments.last()?.ident),
44 _ => None,
45 }
46}
47
48fn extract_instruction_idents(item_fn: &ItemFn) -> Option<Vec<&Ident>> {
49 if item_fn.sig.ident != "process_instruction" {
51 return None;
52 }
53
54 let match_expr = item_fn.block.stmts.iter().find_map(|stmt| {
56 if let Stmt::Expr(Expr::Match(m), ..) = stmt {
57 Some(m)
58 } else {
59 None
60 }
61 })?;
62
63 let instructions = match_expr
65 .arms
66 .iter()
67 .filter_map(|arm| {
68 let Expr::Try(try_expr) = arm.body.as_ref() else {
70 return None;
71 };
72
73 let Expr::Call(call) = try_expr.expr.as_ref() else {
75 return None;
76 };
77
78 let Expr::Path(p) = call.func.as_ref() else {
80 return None;
81 };
82 if p.path.segments.last()?.ident != "handle" {
83 return None;
84 };
85
86 call.args.last().and_then(|arg| {
88 if let Expr::Path(p) = arg {
89 p.path.get_ident()
90 } else {
91 None
92 }
93 })
94 })
95 .collect();
96
97 Some(instructions)
98}