kinetics_parser/
parser.rs1use crate::{
2 params::{Cron, Endpoint, Params, Worker},
3 ParsedFunction, Role,
4};
5use color_eyre::eyre;
6use std::path::{Path, PathBuf};
7use syn::{parse::Parse, visit::Visit, Attribute, ItemFn};
8use walkdir::WalkDir;
9
10#[derive(Clone, Debug, Default)]
13pub struct Package {
14 pub name: String,
16
17 pub relative_path: PathBuf,
19}
20
21#[derive(Debug, Default)]
22pub struct Parser<'a> {
23 pub functions: Vec<ParsedFunction>,
25
26 fn_rel_path: PathBuf,
28
29 pkg: Option<&'a Package>,
31}
32
33impl<'a> Parser<'a> {
34 pub fn new(root_path: &Path, pkg: Option<&'a Package>) -> eyre::Result<Self> {
38 let mut parser: Parser = Default::default();
39
40 if let Some(pkg) = pkg {
41 parser.set_pkg(Some(pkg));
42 parser.walk_dir(&root_path.join(&pkg.relative_path))?;
43 }
44
45 Ok(parser)
46 }
47
48 pub fn walk_dir(&mut self, path: &Path) -> eyre::Result<()> {
49 for entry in WalkDir::new(path)
50 .into_iter()
51 .filter_map(|e| e.ok())
52 .filter(|e| {
53 e.path().strip_prefix(path).is_ok_and(|p| p.starts_with("src/")) && e.path().extension().is_some_and(|ext| ext == "rs") })
56 {
57 let content = std::fs::read_to_string(entry.path())?;
58 let syntax = syn::parse_file(&content)?;
59
60 self.set_fn_rel_path(Some(entry.path().strip_prefix(path)?));
63
64 self.visit_file(&syntax);
65 }
66
67 Ok(())
68 }
69
70 fn set_pkg(&mut self, pkg: Option<&'a Package>) {
71 self.pkg = pkg.to_owned();
72 }
73
74 fn set_fn_rel_path(&mut self, file_path: Option<&Path>) {
75 self.fn_rel_path = file_path.map(PathBuf::from).unwrap_or_default();
76 }
77
78 fn parse_endpoint(&mut self, attr: &Attribute) -> syn::Result<Endpoint> {
79 attr.parse_args_with(Endpoint::parse)
80 }
81
82 fn parse_worker(&mut self, attr: &Attribute) -> syn::Result<Worker> {
83 attr.parse_args_with(Worker::parse)
84 }
85
86 fn parse_cron(&mut self, attr: &Attribute) -> syn::Result<Cron> {
87 attr.parse_args_with(Cron::parse)
88 }
89
90 fn parse_attr_role(&self, input: &Attribute) -> String {
94 let path = input.path();
95
96 if path.segments.len() == 1 {
97 let ident = &path.segments[0].ident;
98 return ident.to_string();
99 }
100
101 if path.segments.len() == 2 && &path.segments[0].ident == "kinetics_macro" {
102 let ident = &path.segments[1].ident;
103 return ident.to_string();
104 }
105
106 "".to_string()
107 }
108}
109
110impl<'a> Visit<'_> for Parser<'a> {
111 fn visit_item_fn(&mut self, item: &ItemFn) {
113 for attr in &item.attrs {
114 let (role, params) = match self.parse_attr_role(attr).as_str() {
116 "endpoint" => {
117 let params = self.parse_endpoint(attr).unwrap();
118 (Role::Endpoint, Params::Endpoint(params))
119 }
120 "worker" => {
121 let params = self.parse_worker(attr).unwrap();
122 (Role::Worker, Params::Worker(params))
123 }
124 "cron" => {
125 let params = self.parse_cron(attr).unwrap();
126 (Role::Cron, Params::Cron(params))
127 }
128 _ => continue,
129 };
130
131 if let Some(pkg) = self.pkg {
132 self.functions.push(ParsedFunction {
133 role,
134 params,
135 rust_function_name: item.sig.ident.to_string(),
136 relative_path: self.fn_rel_path.clone(),
137 pkg_rel_path: pkg.relative_path.clone(),
138 pkg_name: pkg.name.clone(),
139 });
140 }
141 }
142
143 }
145}