context_creator/core/semantic/languages/
rust.rs1use crate::core::semantic::{
4 analyzer::{AnalysisResult, LanguageAnalyzer, SemanticContext, SemanticResult},
5 path_validator::{validate_import_path, validate_module_name},
6 query_engine::QueryEngine,
7 resolver::{ModuleResolver, ResolvedPath, ResolverUtils},
8};
9use crate::utils::error::ContextCreatorError;
10use std::path::Path;
11use tree_sitter::Parser;
12
13#[allow(clippy::new_without_default)]
14pub struct RustAnalyzer {
15 query_engine: QueryEngine,
16}
17
18impl RustAnalyzer {
19 pub fn new() -> Self {
20 let language = tree_sitter_rust::language();
21 let query_engine =
22 QueryEngine::new(language, "rust").expect("Failed to create Rust query engine");
23 Self { query_engine }
24 }
25}
26
27impl LanguageAnalyzer for RustAnalyzer {
28 fn language_name(&self) -> &'static str {
29 "Rust"
30 }
31
32 fn analyze_file(
33 &self,
34 path: &Path,
35 content: &str,
36 context: &SemanticContext,
37 ) -> SemanticResult<AnalysisResult> {
38 let mut parser = Parser::new();
39 parser
40 .set_language(tree_sitter_rust::language())
41 .map_err(|e| ContextCreatorError::ParseError(format!("Failed to set language: {e}")))?;
42
43 let mut result = self
44 .query_engine
45 .analyze_with_parser(&mut parser, content)?;
46
47 self.correlate_types_with_imports(&mut result);
49
50 self.query_engine.resolve_type_definitions(
52 &mut result.type_references,
53 path,
54 &context.base_dir,
55 )?;
56
57 Ok(result)
58 }
59
60 fn can_handle_extension(&self, extension: &str) -> bool {
61 extension == "rs"
62 }
63
64 fn supported_extensions(&self) -> Vec<&'static str> {
65 vec!["rs"]
66 }
67}
68
69impl RustAnalyzer {
70 fn correlate_types_with_imports(&self, result: &mut AnalysisResult) {
72 use std::collections::HashMap;
73
74 let mut type_to_module: HashMap<String, String> = HashMap::new();
76
77 for import in &result.imports {
78 if import.items.is_empty() {
79 if let Some(type_name) = import.module.split("::").last() {
81 if type_name.chars().next().is_some_and(|c| c.is_uppercase()) {
83 type_to_module.insert(type_name.to_string(), import.module.clone());
84 }
85 }
86 } else {
87 for item in &import.items {
89 if item.chars().next().is_some_and(|c| c.is_uppercase()) {
91 type_to_module.insert(item.clone(), import.module.clone());
92 }
93 }
94 }
95 }
96
97 for type_ref in &mut result.type_references {
99 if type_ref.module.is_none() {
100 if let Some(module_path) = type_to_module.get(&type_ref.name) {
101 type_ref.module = Some(module_path.clone());
102 }
103 }
104 }
105 }
106}
107
108pub struct RustModuleResolver;
109
110impl ModuleResolver for RustModuleResolver {
111 fn resolve_import(
112 &self,
113 module_path: &str,
114 from_file: &Path,
115 base_dir: &Path,
116 ) -> Result<ResolvedPath, ContextCreatorError> {
117 validate_module_name(module_path)?;
119
120 if self.is_external_module(module_path) {
122 return Ok(ResolvedPath {
123 path: base_dir.join("Cargo.toml"), is_external: true,
125 confidence: 1.0,
126 });
127 }
128
129 if module_path.starts_with("crate::") {
131 let relative_path = module_path.strip_prefix("crate::").unwrap();
132 let path = ResolverUtils::module_to_path(relative_path);
133 let full_path = base_dir.join("src").join(path);
134
135 if let Some(resolved) = ResolverUtils::find_with_extensions(&full_path, &["rs"]) {
136 let validated_path = validate_import_path(base_dir, &resolved)?;
137 return Ok(ResolvedPath {
138 path: validated_path,
139 is_external: false,
140 confidence: 0.9,
141 });
142 }
143
144 let mod_path = full_path.join("mod.rs");
146 if mod_path.exists() {
147 let validated_path = validate_import_path(base_dir, &mod_path)?;
148 return Ok(ResolvedPath {
149 path: validated_path,
150 is_external: false,
151 confidence: 0.9,
152 });
153 }
154 }
155
156 if module_path.starts_with("self::") || module_path.starts_with("super::") {
158 if let Some(resolved) = ResolverUtils::resolve_relative(module_path, from_file, &["rs"])
159 {
160 return Ok(ResolvedPath {
161 path: resolved,
162 is_external: false,
163 confidence: 0.9,
164 });
165 }
166 }
167
168 if !module_path.contains("::") {
170 if let Some(parent) = from_file.parent() {
171 let file_path = parent.join(format!("{module_path}.rs"));
173 if file_path.exists() {
174 let validated_path = validate_import_path(base_dir, &file_path)?;
175 return Ok(ResolvedPath {
176 path: validated_path,
177 is_external: false,
178 confidence: 0.9,
179 });
180 }
181
182 let mod_path = parent.join(module_path).join("mod.rs");
184 if mod_path.exists() {
185 let validated_path = validate_import_path(base_dir, &mod_path)?;
186 return Ok(ResolvedPath {
187 path: validated_path,
188 is_external: false,
189 confidence: 0.9,
190 });
191 }
192 }
193 }
194
195 Ok(ResolvedPath {
197 path: base_dir.join("Cargo.toml"), is_external: true,
199 confidence: 0.5,
200 })
201 }
202
203 fn get_file_extensions(&self) -> Vec<&'static str> {
204 vec!["rs"]
205 }
206
207 fn is_external_module(&self, module_path: &str) -> bool {
208 let stdlib_crates = ["std", "core", "alloc", "proc_macro", "test"];
210
211 let first_part = module_path.split("::").next().unwrap_or(module_path);
213
214 if stdlib_crates.contains(&first_part) {
216 return true;
217 }
218
219 if !module_path.contains("::") {
221 return false;
222 }
223
224 if module_path.starts_with("crate::")
226 || module_path.starts_with("self::")
227 || module_path.starts_with("super::")
228 {
229 return false;
230 }
231
232 true
235 }
236}