kinetics_parser/
parser.rs1use crate::{environment::Environment, Cron, Endpoint, Worker};
2use color_eyre::eyre;
3use serde::{Deserialize, Serialize};
4use std::fmt::Display;
5use std::path::PathBuf;
6use syn::{parse::Parse, visit::Visit, Attribute, ItemFn};
7use walkdir::WalkDir;
8
9#[derive(Debug, Clone)]
11pub struct ParsedFunction {
12 pub rust_function_name: String,
14
15 pub relative_path: String,
17
18 pub role: Role,
20}
21
22impl ParsedFunction {
23 pub fn path_to_name(path: &str) -> String {
25 path.split(&['.', '/'])
26 .filter(|s| !s.eq(&"rs"))
27 .map(|s| match s.chars().next() {
28 Some(first) => first.to_uppercase().collect::<String>() + &s[1..],
29 None => String::new(),
30 })
31 .collect::<String>()
32 .replacen("Src", "", 1)
33 }
34
35 pub fn func_name(&self, is_local: bool) -> eyre::Result<String> {
41 let rust_name = &self.rust_function_name;
42 let full_path = format!("{}/{rust_name}", self.relative_path);
43 let default_func_name = Self::path_to_name(&full_path);
44 let name = self.role.name().unwrap_or(&default_func_name);
45
46 if name.len() > 64 {
47 Err(eyre::eyre!(
48 "Function name is longer than 64 chars: {}",
49 name
50 ))
51 } else {
52 Ok(format!("{}{}", name, if is_local { "Local" } else { "" }))
54 }
55 }
56}
57
58#[derive(Debug, Clone, Serialize, Deserialize)]
59pub enum Role {
60 Endpoint(Endpoint),
61 Cron(Cron),
62 Worker(Worker),
63}
64
65impl Display for Role {
66 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
67 let str = match self {
68 Role::Endpoint(_) => "endpoint",
69 Role::Cron(_) => "cron",
70 Role::Worker(_) => "worker",
71 };
72
73 write!(f, "{}", str)
74 }
75}
76
77impl Role {
78 pub fn name(&self) -> Option<&String> {
79 match self {
80 Role::Endpoint(params) => params.name.as_ref(),
81 Role::Cron(params) => params.name.as_ref(),
82 Role::Worker(params) => params.name.as_ref(),
83 }
84 }
85
86 pub fn environment(&self) -> &Environment {
87 match self {
88 Role::Endpoint(params) => ¶ms.environment,
89 Role::Cron(params) => ¶ms.environment,
90 Role::Worker(params) => ¶ms.environment,
91 }
92 }
93}
94
95#[derive(Debug, Default)]
96pub struct Parser {
97 pub functions: Vec<ParsedFunction>,
99
100 pub relative_path: String,
102}
103
104impl Parser {
105 pub fn new(path: Option<&PathBuf>) -> eyre::Result<Self> {
109 let mut parser: Parser = Default::default();
110
111 if let Some(path) = path {
112 parser.walk_dir(path)?;
113 }
114
115 Ok(parser)
116 }
117
118 pub fn walk_dir(&mut self, path: &PathBuf) -> eyre::Result<()> {
119 for entry in WalkDir::new(path)
120 .into_iter()
121 .filter_map(|e| e.ok())
122 .filter(|e| {
123 e.path().strip_prefix(path).is_ok_and(|p| p.starts_with("src/")) && e.path().extension().is_some_and(|ext| ext == "rs") })
126 {
127 let content = std::fs::read_to_string(entry.path())?;
128 let syntax = syn::parse_file(&content)?;
129
130 self.set_relative_path(entry.path().strip_prefix(path)?.to_str());
133
134 self.visit_file(&syntax);
135 }
136
137 Ok(())
138 }
139
140 pub fn set_relative_path(&mut self, file_path: Option<&str>) {
141 self.relative_path = file_path.map_or_else(|| "".to_string(), |s| s.to_string());
142 }
143
144 fn parse_endpoint(&mut self, attr: &Attribute) -> syn::Result<Endpoint> {
145 attr.parse_args_with(Endpoint::parse)
146 }
147
148 fn parse_worker(&mut self, attr: &Attribute) -> syn::Result<Worker> {
149 attr.parse_args_with(Worker::parse)
150 }
151
152 fn parse_cron(&mut self, attr: &Attribute) -> syn::Result<Cron> {
153 attr.parse_args_with(Cron::parse)
154 }
155
156 fn parse_attr_role(&self, input: &Attribute) -> String {
161 let path = input.path();
162
163 if path.segments.len() == 1 {
164 let ident = &path.segments[0].ident;
165 return ident.to_string();
166 }
167
168 if path.segments.len() == 2 && &path.segments[0].ident == "kinetics_macro" {
169 let ident = &path.segments[1].ident;
170 return ident.to_string();
171 }
172
173 "".to_string()
174 }
175}
176
177impl Visit<'_> for Parser {
178 fn visit_item_fn(&mut self, item: &ItemFn) {
180 for attr in &item.attrs {
181 let role = match self.parse_attr_role(attr).as_str() {
183 "endpoint" => {
184 let params = self.parse_endpoint(attr).unwrap();
185 Role::Endpoint(params)
186 }
187 "worker" => {
188 let params = self.parse_worker(attr).unwrap();
189 Role::Worker(params)
190 }
191 "cron" => {
192 let params = self.parse_cron(attr).unwrap();
193 Role::Cron(params)
194 }
195 _ => continue,
196 };
197
198 self.functions.push(ParsedFunction {
199 role,
200 rust_function_name: item.sig.ident.to_string(),
201 relative_path: self.relative_path.clone(),
202 });
203 }
204
205 }
207}