kinetics_parser/
parser.rs1use std::path::PathBuf;
2
3use crate::{environment::Environment, Cron, Endpoint, Worker};
4use color_eyre::eyre;
5use syn::{parse::Parse, visit::Visit, Attribute, ItemFn};
6use walkdir::WalkDir;
7
8#[derive(Debug, Clone)]
10pub struct ParsedFunction {
11 pub rust_function_name: String,
13
14 pub relative_path: String,
16
17 pub role: Role,
19}
20
21impl ParsedFunction {
22 pub fn func_name(&self, is_local: bool) -> String {
28 let rust_name = &self.rust_function_name;
29 let full_path = format!("{}/{rust_name}", self.relative_path);
30
31 let default_func_name = full_path
32 .as_str()
33 .replace("_", "Undrscr")
34 .replace("_", "Dash")
35 .split(&['.', '/'])
36 .filter(|s| !s.eq(&"rs"))
37 .map(|s| match s.chars().next() {
38 Some(first) => first.to_uppercase().collect::<String>() + &s[1..],
39 None => String::new(),
40 })
41 .collect::<String>()
42 .replacen("Src", "", 1);
43
44 format!(
46 "{}{}",
47 self.role.name().unwrap_or(&default_func_name),
48 if is_local { "Local" } else { "" }
49 )
50 }
51}
52
53#[derive(Debug, Clone)]
54pub enum Role {
55 Endpoint(Endpoint),
56 Cron(Cron),
57 Worker(Worker),
58}
59
60impl Role {
61 pub fn name(&self) -> Option<&String> {
62 match self {
63 Role::Endpoint(params) => params.name.as_ref(),
64 Role::Cron(params) => params.name.as_ref(),
65 Role::Worker(params) => params.name.as_ref(),
66 }
67 }
68
69 pub fn environment(&self) -> &Environment {
70 match self {
71 Role::Endpoint(params) => ¶ms.environment,
72 Role::Cron(params) => ¶ms.environment,
73 Role::Worker(params) => ¶ms.environment,
74 }
75 }
76}
77
78#[derive(Debug, Default)]
79pub struct Parser {
80 pub functions: Vec<ParsedFunction>,
82
83 pub relative_path: String,
85}
86
87impl Parser {
88 pub fn new(path: Option<&PathBuf>) -> eyre::Result<Self> {
92 let mut parser: Parser = Default::default();
93
94 if path.is_some() {
95 parser.walk_dir(path.unwrap())?;
96 }
97
98 Ok(parser)
99 }
100
101 pub fn walk_dir(&mut self, path: &PathBuf) -> eyre::Result<()> {
102 for entry in WalkDir::new(path)
103 .into_iter()
104 .filter_map(|e| e.ok())
105 .filter(|e| e.path().extension().is_some_and(|ext| ext == "rs"))
106 {
107 let content = std::fs::read_to_string(entry.path())?;
108 let syntax = syn::parse_file(&content)?;
109
110 self.set_relative_path(entry.path().strip_prefix(path)?.to_str());
113
114 self.visit_file(&syntax);
115 }
116
117 Ok(())
118 }
119
120 pub fn set_relative_path(&mut self, file_path: Option<&str>) {
121 self.relative_path = file_path.map_or_else(|| "".to_string(), |s| s.to_string());
122 }
123
124 fn parse_endpoint(&mut self, attr: &Attribute) -> syn::Result<Endpoint> {
125 attr.parse_args_with(Endpoint::parse)
126 }
127
128 fn parse_worker(&mut self, attr: &Attribute) -> syn::Result<Worker> {
129 attr.parse_args_with(Worker::parse)
130 }
131
132 fn parse_cron(&mut self, attr: &Attribute) -> syn::Result<Cron> {
133 attr.parse_args_with(Cron::parse)
134 }
135
136 fn parse_attr_role(&self, input: &Attribute) -> String {
141 let path = input.path();
142
143 if path.segments.len() == 1 {
144 let ident = &path.segments[0].ident;
145 return ident.to_string();
146 }
147
148 if path.segments.len() == 2 && &path.segments[0].ident == "kinetics_macro" {
149 let ident = &path.segments[1].ident;
150 return ident.to_string();
151 }
152
153 "".to_string()
154 }
155}
156
157impl Visit<'_> for Parser {
158 fn visit_item_fn(&mut self, item: &ItemFn) {
160 for attr in &item.attrs {
161 let role = match self.parse_attr_role(attr).as_str() {
163 "endpoint" => {
164 let params = self.parse_endpoint(attr).unwrap();
165 Role::Endpoint(params)
166 }
167 "worker" => {
168 let params = self.parse_worker(attr).unwrap();
169 Role::Worker(params)
170 }
171 "cron" => {
172 let params = self.parse_cron(attr).unwrap();
173 Role::Cron(params)
174 }
175 _ => continue,
176 };
177
178 self.functions.push(ParsedFunction {
179 role,
180 rust_function_name: item.sig.ident.to_string(),
181 relative_path: self.relative_path.clone(),
182 });
183 }
184
185 }
187}