use shape_ast::ast::ImportStmt;
use shape_ast::error::{Result, ShapeError};
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use super::{Export, Module};
pub(super) struct ModuleCache {
module_cache: HashMap<String, Arc<Module>>,
loading_stack: Vec<String>,
dependencies: HashMap<String, Vec<String>>,
}
impl ModuleCache {
pub(super) fn new() -> Self {
Self {
module_cache: HashMap::new(),
loading_stack: Vec::new(),
dependencies: HashMap::new(),
}
}
pub(super) fn get(&self, module_path: &str) -> Option<Arc<Module>> {
self.module_cache.get(module_path).cloned()
}
pub(super) fn check_circular_dependency(&self, module_path: &str) -> Result<()> {
if self.loading_stack.contains(&module_path.to_string()) {
if self.loading_stack.last().map(|s| s.as_str()) == Some(module_path)
&& self
.loading_stack
.iter()
.filter(|s| s.as_str() == module_path)
.count()
== 1
{
return Ok(());
}
let cycle = self.loading_stack.join(" -> ") + " -> " + module_path;
return Err(ShapeError::ModuleError {
message: format!("Circular dependency detected: {}", cycle),
module_path: None,
});
}
Ok(())
}
pub(super) fn push_loading(&mut self, module_path: String) {
self.loading_stack.push(module_path);
}
pub(super) fn pop_loading(&mut self) {
self.loading_stack.pop();
}
pub(super) fn store_dependencies(&mut self, module_path: String, dependencies: Vec<String>) {
self.dependencies.insert(module_path, dependencies);
}
pub(super) fn insert(&mut self, module_path: String, module: Arc<Module>) {
self.module_cache.insert(module_path, module);
}
pub(super) fn loaded_modules(&self) -> Vec<&str> {
self.module_cache.keys().map(|s| s.as_str()).collect()
}
pub(super) fn get_export(&self, module_path: &str, export_name: &str) -> Option<&Export> {
self.module_cache.get(module_path)?.exports.get(export_name)
}
pub(super) fn get_module(&self, module_path: &str) -> Option<&Arc<Module>> {
self.module_cache.get(module_path)
}
pub(super) fn clear(&mut self) {
self.module_cache.clear();
self.dependencies.clear();
self.loading_stack.clear();
}
pub(super) fn get_dependencies(&self, module_path: &str) -> Option<&Vec<String>> {
self.dependencies.get(module_path)
}
pub(super) fn get_all_dependencies(&self, module_path: &str) -> Vec<String> {
let mut all_deps = Vec::new();
let mut visited = HashSet::new();
self.collect_dependencies_recursive(module_path, &mut all_deps, &mut visited);
all_deps
}
fn collect_dependencies_recursive(
&self,
module_path: &str,
all_deps: &mut Vec<String>,
visited: &mut HashSet<String>,
) {
if visited.contains(module_path) {
return;
}
visited.insert(module_path.to_string());
if let Some(deps) = self.dependencies.get(module_path) {
for dep in deps {
if !all_deps.contains(dep) {
all_deps.push(dep.clone());
}
self.collect_dependencies_recursive(dep, all_deps, visited);
}
}
}
}
pub(super) fn resolve_import(
import_stmt: &ImportStmt,
module: &Arc<Module>,
) -> Result<HashMap<String, Export>> {
let mut imports = HashMap::new();
match &import_stmt.items {
shape_ast::ast::ImportItems::Named(specs) => {
for spec in specs {
let export_name = &spec.name;
let local_name = spec.alias.as_ref().unwrap_or(&spec.name);
if let Some(export) = module.exports.get(export_name) {
imports.insert(local_name.clone(), export.clone());
} else {
return Err(ShapeError::ModuleError {
message: format!(
"Module '{}' has no export named '{}'",
import_stmt.from, export_name
),
module_path: None,
});
}
}
}
shape_ast::ast::ImportItems::Namespace { .. } => {
}
}
Ok(imports)
}