use std::collections::HashMap;
use super::file_table::FileId;
use super::imports_go::GoModules;
use super::imports_swift::swift_module_root;
#[derive(Debug, Default, Clone)]
pub(crate) struct ModuleIndex {
go_modules: GoModules,
by_root: HashMap<String, Vec<FileId>>,
by_name: HashMap<String, Vec<String>>,
home: HashMap<FileId, String>,
}
impl ModuleIndex {
pub(crate) fn build(path_to_id: &HashMap<String, FileId>) -> Self {
let mut index = ModuleIndex::default();
for (path, id) in path_to_id {
let Some(root) = module_root(path) else {
continue;
};
index.by_root.entry(root.clone()).or_default().push(*id);
index.home.insert(*id, root);
}
for (root, files) in &mut index.by_root {
files.sort_unstable();
if root.contains("/Sources/")
|| root.contains("/Tests/")
|| root.starts_with("Sources/")
|| root.starts_with("Tests/")
{
let name = root.rsplit('/').next().unwrap_or(root).to_string();
index.by_name.entry(name).or_default().push(root.clone());
}
}
for roots in index.by_name.values_mut() {
roots.sort();
}
index
}
pub(crate) fn roots_named(&self, name: &str) -> &[String] {
self.by_name.get(name).map(Vec::as_slice).unwrap_or(&[])
}
pub(crate) fn files_in_roots(&self, roots: &[String]) -> Vec<FileId> {
let mut out: Vec<FileId> = roots
.iter()
.filter_map(|root| self.by_root.get(root))
.flatten()
.copied()
.collect();
out.sort_unstable();
out.dedup();
out
}
pub(crate) fn files_named(&self, name: &str) -> Vec<FileId> {
self.files_in_roots(self.roots_named(name))
}
pub(crate) fn with_go_modules(mut self, go_modules: GoModules) -> Self {
self.go_modules = go_modules;
self
}
pub(crate) fn go_root_for_import(&self, import_path: &str) -> Option<String> {
let dir = self.go_modules.directory_for(import_path)?;
self.by_root.contains_key(&dir).then_some(dir)
}
pub(crate) fn go_files_for_import(&self, import_path: &str) -> Vec<FileId> {
let Some(dir) = self.go_root_for_import(import_path) else {
return Vec::new();
};
self.files_in_roots(std::slice::from_ref(&dir))
}
pub(crate) fn home_of(&self, file: FileId) -> Option<&str> {
self.home.get(&file).map(String::as_str)
}
pub(crate) fn siblings_of(&self, file: FileId) -> Vec<FileId> {
let Some(root) = self.home.get(&file) else {
return Vec::new();
};
self.by_root
.get(root)
.map(|files| files.iter().copied().filter(|id| *id != file).collect())
.unwrap_or_default()
}
}
fn module_root(path: &str) -> Option<String> {
if path.ends_with(".go") {
return Some(
path.rsplit_once('/')
.map(|(head, _)| head.to_string())
.unwrap_or_default(),
);
}
swift_module_root(path)
}