use std::collections::{BTreeMap, BTreeSet, HashMap};
use std::path::{Path, PathBuf};
use super::extractor::extract_imports;
use super::graph::Source;
use super::layout::Layout;
pub struct Packages {
by_dir: HashMap<PathBuf, Vec<usize>>,
modules: Vec<(PathBuf, String)>,
}
fn offered(path: &Path) -> bool {
let name = path.file_name().unwrap_or_default().to_string_lossy();
!name.ends_with("_test.go") && !name.starts_with(['_', '.'])
}
impl Packages {
pub fn of(sources: &[Source], layout: &Layout) -> Self {
let mut by_dir: HashMap<PathBuf, Vec<usize>> = HashMap::new();
for (file, source) in sources.iter().enumerate() {
if source.language == "Go" && offered(&source.path) {
let dir = source.path.parent().unwrap_or(Path::new(""));
by_dir.entry(dir.to_path_buf()).or_default().push(file);
}
}
let mut modules = layout.go_modules.clone();
modules.sort_by(|a, b| b.1.len().cmp(&a.1.len()).then_with(|| a.cmp(b)));
Packages { by_dir, modules }
}
fn directory(&self, import: &str) -> Option<PathBuf> {
self.modules.iter().find_map(|(dir, module)| {
let rest = import.strip_prefix(module.as_str())?;
match rest.strip_prefix('/') {
Some(package) => Some(dir.join(package)),
None => rest.is_empty().then(|| dir.clone()),
}
})
}
pub fn uses(&self, source: &Source) -> Option<BTreeMap<usize, BTreeSet<String>>> {
(source.language == "Go").then(|| {
extract_imports(&source.language, &source.text)
.iter()
.filter_map(|import| self.directory(import))
.filter_map(|dir| self.by_dir.get(&dir))
.flatten()
.map(|&file| (file, BTreeSet::new()))
.collect()
})
}
}
#[cfg(test)]
#[path = "golang_test.rs"]
mod tests;