use std::collections::HashSet;
use std::fs;
use std::path::{Path, PathBuf};
use crate::types::{EdgeKind, Language, Node, NodeKind};
use super::helpers::{js_family_extension, supertype_bearing};
use super::{ImportMapping, ReExport, ReferenceResolver, ResolutionContext};
use crate::resolution::go_module::{GoModule, load_go_module};
use crate::resolution::import_resolver::{
extract_import_mappings, extract_re_exports, load_cpp_include_dirs,
};
use crate::resolution::path_aliases::{AliasMap, load_project_aliases};
use crate::resolution::workspace_packages::{WorkspacePackages, load_workspace_packages};
impl<'db> ResolutionContext for ReferenceResolver<'db> {
fn get_nodes_in_file(&mut self, file_path: &str) -> Vec<Node> {
let key = file_path.to_string();
if let Some(cached) = self.node_cache.get(&key) {
return cached;
}
let result = self
.queries
.get_nodes_by_file(file_path)
.unwrap_or_default();
self.node_cache.set(key, result.clone());
result
}
fn get_nodes_by_name(&mut self, name: &str) -> Vec<Node> {
let key = name.to_string();
if let Some(cached) = self.name_cache.get(&key) {
return cached;
}
let result = self.queries.get_nodes_by_name(name).unwrap_or_default();
self.name_cache.set(key, result.clone());
result
}
fn get_nodes_by_qualified_name(&mut self, qualified_name: &str) -> Vec<Node> {
let key = qualified_name.to_string();
if let Some(cached) = self.qualified_name_cache.get(&key) {
return cached;
}
let result = self
.queries
.get_nodes_by_qualified_name_exact(qualified_name)
.unwrap_or_default();
self.qualified_name_cache.set(key, result.clone());
result
}
fn get_nodes_by_kind(&mut self, kind: NodeKind) -> Vec<Node> {
self.queries.get_nodes_by_kind(kind).unwrap_or_default()
}
fn file_exists(&mut self, file_path: &str) -> bool {
if let Some(known_files) = &self.known_files {
let normalized = file_path.replace('\\', "/");
if known_files.contains(file_path) || known_files.contains(&normalized) {
return true;
}
}
fs::metadata(Path::new(&self.project_root).join(file_path)).is_ok()
}
fn read_file(&mut self, file_path: &str) -> Option<String> {
let key = file_path.to_string();
if let Some(cached) = self.file_cache.get(&key) {
return cached;
}
let content = fs::read_to_string(Path::new(&self.project_root).join(file_path)).ok();
self.file_cache.set(key, content.clone());
content
}
fn get_project_root(&self) -> String {
self.project_root.clone()
}
fn get_all_files(&mut self) -> Vec<String> {
self.queries.get_all_file_paths().unwrap_or_default()
}
fn get_nodes_by_lower_name(&mut self, lower_name: &str) -> Vec<Node> {
let key = lower_name.to_string();
if let Some(cached) = self.lower_name_cache.get(&key) {
return cached;
}
let result = self
.queries
.get_nodes_by_lower_name(lower_name)
.unwrap_or_default();
self.lower_name_cache.set(key, result.clone());
result
}
fn get_supertypes(&mut self, type_name: &str, language: Language) -> Vec<String> {
let type_nodes = self
.get_nodes_by_name(type_name)
.into_iter()
.filter(|node| supertype_bearing(node.kind) && node.language == language)
.collect::<Vec<_>>();
let mut supertypes = HashSet::new();
for type_node in type_nodes {
for edge in self
.queries
.get_outgoing_edges(
&type_node.id,
Some(vec![EdgeKind::Implements, EdgeKind::Extends]),
None,
)
.unwrap_or_default()
{
if let Some(target) = self.queries.get_node_by_id(&edge.target).unwrap_or(None)
&& !target.name.is_empty()
&& target.name != type_name
{
supertypes.insert(target.name);
}
}
}
supertypes.into_iter().collect()
}
fn get_node_by_id(&mut self, id: &str) -> Option<Node> {
self.queries.get_node_by_id(id).unwrap_or(None)
}
fn get_import_mappings(&mut self, file_path: &str, language: Language) -> Vec<ImportMapping> {
let key = file_path.to_string();
if let Some(cached) = self.import_mapping_cache.get(&key) {
return cached;
}
let Some(content) = self.read_file(file_path) else {
self.import_mapping_cache.set(key, Vec::new());
return Vec::new();
};
let mappings = extract_import_mappings(file_path, &content, language);
self.import_mapping_cache.set(key, mappings.clone());
mappings
}
fn get_project_aliases(&mut self) -> Option<AliasMap> {
if !self.project_aliases_loaded {
self.project_aliases = load_project_aliases(&self.project_root);
self.project_aliases_loaded = true;
}
self.project_aliases.clone()
}
fn get_go_module(&mut self) -> Option<GoModule> {
if !self.go_module_loaded {
self.go_module = load_go_module(&self.project_root);
self.go_module_loaded = true;
}
self.go_module.clone()
}
fn get_workspace_packages(&mut self) -> Option<WorkspacePackages> {
if !self.workspace_packages_loaded {
self.workspace_packages = load_workspace_packages(&self.project_root);
self.workspace_packages_loaded = true;
}
self.workspace_packages.clone()
}
fn get_re_exports(&mut self, file_path: &str, language: Language) -> Vec<ReExport> {
let key = file_path.to_string();
if let Some(cached) = self.re_export_cache.get(&key) {
return cached;
}
let Some(content) = self.read_file(file_path) else {
self.re_export_cache.set(key, Vec::new());
return Vec::new();
};
let is_js_family = js_family_extension(file_path);
let re_exports = extract_re_exports(
&content,
if is_js_family {
Language::TypeScript
} else {
language
},
);
self.re_export_cache.set(key, re_exports.clone());
re_exports
}
fn list_directories(&mut self, relative_path: &str) -> Vec<String> {
let target = if relative_path == "." || relative_path.is_empty() {
PathBuf::from(&self.project_root)
} else {
Path::new(&self.project_root).join(relative_path)
};
fs::read_dir(target)
.ok()
.into_iter()
.flat_map(|entries| entries.filter_map(Result::ok))
.filter(|entry| entry.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
.map(|entry| entry.file_name().to_string_lossy().into_owned())
.collect()
}
fn get_cpp_include_dirs(&mut self) -> Vec<String> {
load_cpp_include_dirs(&self.project_root)
}
}