AirLibrary/Indexing/Language/
ParseRust.rs1use std::path::PathBuf;
67
68use crate::Indexing::State::CreateState::{SymbolInfo, SymbolKind};
69
70pub fn ExtractRustSymbols(content:&str, file_path:&PathBuf) -> Vec<SymbolInfo> {
72 let mut symbols = Vec::new();
73
74 let lines:Vec<&str> = content.lines().collect();
75
76 for (line_idx, line) in lines.iter().enumerate() {
77 let line_content = line.trim();
78
79 let line_num = line_idx as u32 + 1;
80
81 if line_content.starts_with("//") || line_content.starts_with("/*") || line_content.starts_with("*") {
83 continue;
84 }
85
86 symbols.extend(ExtractRustSymbolsFromLine(line_content, line_num, line, file_path));
88 }
89
90 symbols
91}
92
93fn ExtractRustSymbolsFromLine(line_content:&str, line_num:u32, line:&str, file_path:&PathBuf) -> Vec<SymbolInfo> {
95 let mut symbols = Vec::new();
96
97 if let Some(rest) = line_content.strip_prefix("struct ") {
99 let name = rest.split_whitespace().next().unwrap_or("").trim_end_matches('{');
100
101 if !name.is_empty() {
102 if let Some(col) = line.find("struct") {
103 symbols.push(SymbolInfo {
104 name:name.to_string(),
105 kind:SymbolKind::Struct,
106 line:line_num,
107 column:col as u32,
108 full_path:format!("{}::{}", file_path.display(), name),
109 });
110 }
111 }
112 }
113
114 if let Some(rest) = line_content.strip_prefix("impl ") {
116 let name = rest.split_whitespace().next().unwrap_or("").trim_end_matches('{');
117
118 if !name.is_empty() {
119 if let Some(col) = line.find("impl") {
120 symbols.push(SymbolInfo {
121 name:name.to_string(),
122 kind:SymbolKind::Method,
123 line:line_num,
124 column:col as u32,
125 full_path:format!("{}::{}::", file_path.display(), name),
126 });
127 }
128 }
129 }
130
131 if let Some(rest) = line_content.strip_prefix("fn ") {
133 let name = rest.split(|c| c == '(' || c == '<' || c == ':').next().unwrap_or("").trim();
134
135 if !name.is_empty() {
136 if let Some(col) = line.find("fn") {
137 symbols.push(SymbolInfo {
138 name:name.to_string(),
139 kind:SymbolKind::Function,
140 line:line_num,
141 column:col as u32,
142 full_path:format!("{}::{}", file_path.display(), name),
143 });
144 }
145 }
146 }
147
148 if let Some(rest) = line_content.strip_prefix("mod ") {
150 let name = rest.split_whitespace().next().unwrap_or("").trim_end_matches('{');
151
152 if !name.is_empty() {
153 if let Some(col) = line.find("mod") {
154 symbols.push(SymbolInfo {
155 name:name.to_string(),
156 kind:SymbolKind::Module,
157 line:line_num,
158 column:col as u32,
159 full_path:format!("{}::{}::", file_path.display(), name),
160 });
161 }
162 }
163 }
164
165 if let Some(rest) = line_content.strip_prefix("enum ") {
167 let name = rest.split_whitespace().next().unwrap_or("").trim_end_matches('{');
168
169 if !name.is_empty() {
170 if let Some(col) = line.find("enum") {
171 symbols.push(SymbolInfo {
172 name:name.to_string(),
173 kind:SymbolKind::Enum,
174 line:line_num,
175 column:col as u32,
176 full_path:format!("{}::{}", file_path.display(), name),
177 });
178 }
179 }
180 }
181
182 if let Some(rest) = line_content.strip_prefix("trait ") {
184 let name = rest.split_whitespace().next().unwrap_or("").trim_end_matches('{');
185
186 if !name.is_empty() {
187 if let Some(col) = line.find("trait") {
188 symbols.push(SymbolInfo {
189 name:name.to_string(),
190 kind:SymbolKind::Interface,
191 line:line_num,
192 column:col as u32,
193 full_path:format!("{}::{}", file_path.display(), name),
194 });
195 }
196 }
197 }
198
199 if let Some(rest) = line_content.strip_prefix("type ") {
201 let name = rest.split('=').next().unwrap_or("").trim().trim_end_matches(';');
202
203 if !name.is_empty() {
204 if let Some(col) = line.find("type") {
205 symbols.push(SymbolInfo {
206 name:name.to_string(),
207 kind:SymbolKind::TypeParameter,
208 line:line_num,
209 column:col as u32,
210 full_path:format!("{}::{}", file_path.display(), name),
211 });
212 }
213 }
214 }
215
216 if line_content.starts_with("const ") && !line_content.contains('=') {
218 if let Some(rest) = line_content.strip_prefix("const ") {
219 let name = rest.split(|c| c == ':' || c == '=').next().unwrap_or("").trim();
220
221 if !name.is_empty() {
222 if let Some(col) = line.find("const") {
223 symbols.push(SymbolInfo {
224 name:name.to_string(),
225 kind:SymbolKind::Constant,
226 line:line_num,
227 column:col as u32,
228 full_path:format!("{}::{}", file_path.display(), name),
229 });
230 }
231 }
232 }
233 }
234
235 if line_content.starts_with("static ") {
237 if let Some(rest) = line_content.strip_prefix("static ") {
238 let name = rest.split(|c| c == ':' || c == '=').next().unwrap_or("").trim();
239
240 if !name.is_empty() {
241 if let Some(col) = line.find("static") {
242 symbols.push(SymbolInfo {
243 name:name.to_string(),
244 kind:SymbolKind::Variable,
245 line:line_num,
246 column:col as u32,
247 full_path:format!("{}::{}", file_path.display(), name),
248 });
249 }
250 }
251 }
252 }
253
254 symbols
255}
256
257pub fn IsRustStruct(line:&str) -> bool {
259 let trimmed = line.trim();
260
261 let after_keywords = trimmed
262 .strip_prefix("pub ")
263 .or_else(|| trimmed.strip_prefix("unsafe "))
264 .or_else(|| trimmed.strip_prefix("pub(crate) "))
265 .unwrap_or(trimmed);
266
267 after_keywords.starts_with("struct ")
268}
269
270pub fn IsRustFunction(line:&str) -> bool {
272 let trimmed = line.trim();
273
274 let after_keywords = trimmed
275 .strip_prefix("pub ")
276 .or_else(|| trimmed.strip_prefix("pub(crate) "))
277 .or_else(|| trimmed.strip_prefix("unsafe "))
278 .or_else(|| trimmed.strip_prefix("async "))
279 .unwrap_or(trimmed);
280
281 after_keywords.starts_with("fn ")
282}
283
284pub fn IsRustImpl(line:&str) -> bool {
286 let trimmed = line.trim();
288
289 let after_keywords = trimmed
290 .strip_prefix("pub ")
291 .or_else(|| trimmed.strip_prefix("unsafe "))
292 .unwrap_or(trimmed);
293
294 after_keywords.starts_with("impl ")
295}
296
297pub fn ExtractVisibilityModifier(line:&str) -> Option<&str> {
299 let trimmed = line.trim();
300
301 if trimmed.starts_with("pub ") {
302 Some("pub")
303 } else if trimmed.starts_with("pub(crate) ") {
304 Some("pub(crate)")
305 } else if trimmed.starts_with("pub(super) ") {
306 Some("pub(super)")
307 } else if trimmed.starts_with("pub(in ") {
308 let rest = trimmed.strip_prefix("pub(in ").unwrap_or("");
310
311 let path = rest.split(')').next().unwrap_or("");
312
313 if !path.is_empty() {
314 Some(&trimmed[0..trimmed.find(')').unwrap_or(trimmed.len()) + 1])
315 } else {
316 None
317 }
318 } else {
319 None
320 }
321}