use crate::eval::{Value, Environment};
use crate::module_system::{
loader::ModuleLoader,
ModuleId,
cache::ModuleCache,
Module
};
use crate::diagnostics::{Error, Result};
use std::collections::HashMap;
use std::rc::Rc;
use std::time::SystemTime;
use std::path::PathBuf;
#[derive(Debug)]
pub struct ModuleManager {
loader: ModuleLoader,
cache: ModuleCache,
loaded_modules: HashMap<ModuleId, LoadedModule>,
dependencies: HashMap<ModuleId, Vec<ModuleId>>,
hooks: ModuleHooks,
}
#[derive(Debug, Clone)]
pub struct LoadedModule {
pub module: Module,
pub environment: Rc<Environment>,
pub loaded_at: SystemTime,
pub load_count: usize,
pub dependencies: Vec<ModuleId>,
pub dependents: Vec<ModuleId>,
}
type PreLoadHook = Box<dyn Fn(&ModuleId) -> Result<()>>;
type PostLoadHook = Box<dyn Fn(&ModuleId, &LoadedModule) -> Result<()>>;
type PreUnloadHook = Box<dyn Fn(&ModuleId) -> Result<()>>;
type PostUnloadHook = Box<dyn Fn(&ModuleId) -> Result<()>>;
pub struct ModuleHooks {
pub pre_load: Vec<PreLoadHook>,
pub post_load: Vec<PostLoadHook>,
pub pre_unload: Vec<PreUnloadHook>,
pub post_unload: Vec<PostUnloadHook>,
}
impl std::fmt::Debug for ModuleHooks {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ModuleHooks")
.field("pre_load", &format!("{count} hook(s)", count = self.pre_load.len()))
.field("post_load", &format!("{count} hook(s)", count = self.post_load.len()))
.field("pre_unload", &format!("{count} hook(s)", count = self.pre_unload.len()))
.field("post_unload", &format!("{count} hook(s)", count = self.post_unload.len()))
.finish()
}
}
impl ModuleManager {
pub fn new() -> Result<Self> {
Ok(Self {
loader: ModuleLoader::new()?,
cache: ModuleCache::new(),
loaded_modules: HashMap::new(),
dependencies: HashMap::new(),
hooks: ModuleHooks::new(),
})
}
pub fn load_module(&mut self, module_id: ModuleId, path: Option<PathBuf>) -> Result<Rc<Environment>> {
for hook in &self.hooks.pre_load {
hook(&module_id)?;
}
if let Some(loaded) = self.loaded_modules.get_mut(&module_id) {
loaded.load_count += 1;
return Ok(loaded.environment.clone());
}
let effective_module_id = if let Some(_path) = path {
module_id.clone()
} else {
module_id.clone()
};
let module = self.loader.load(&effective_module_id)?;
let module_env = Rc::new(Environment::new(None, 0));
for _value in module.exports.values() {
}
let loaded_module = LoadedModule {
module,
environment: module_env.clone(),
loaded_at: SystemTime::now(),
load_count: 1,
dependencies: Vec::new(),
dependents: Vec::new(),
};
for hook in &self.hooks.post_load {
hook(&module_id, &loaded_module)?;
}
self.loaded_modules.insert(module_id, loaded_module);
Ok(module_env)
}
pub fn unload_module(&mut self, module_id: &ModuleId) -> Result<()> {
if let Some(loaded) = self.loaded_modules.get_mut(module_id) {
loaded.load_count = loaded.load_count.saturating_sub(1);
if loaded.load_count == 0 {
for hook in &self.hooks.pre_unload {
hook(module_id)?;
}
self.loaded_modules.remove(module_id);
for hook in &self.hooks.post_unload {
hook(module_id)?;
}
}
}
Ok(())
}
pub fn get_module(&self, module_id: &ModuleId) -> Option<&LoadedModule> {
self.loaded_modules.get(module_id)
}
pub fn loaded_modules(&self) -> Vec<&ModuleId> {
self.loaded_modules.keys().collect()
}
pub fn add_dependency(&mut self, dependent: ModuleId, dependency: ModuleId) {
self.dependencies.entry(dependent.clone()).or_default().push(dependency.clone());
if let Some(dep_module) = self.loaded_modules.get_mut(&dependency) {
if !dep_module.dependents.contains(&dependent) {
dep_module.dependents.push(dependent);
}
}
}
pub fn get_dependencies(&self, module_id: &ModuleId) -> Vec<&ModuleId> {
self.dependencies.get(module_id)
.map(|deps| deps.iter().collect())
.unwrap_or_default()
}
pub fn add_pre_load_hook<F>(&mut self, hook: F)
where
F: Fn(&ModuleId) -> Result<()> + 'static,
{
self.hooks.pre_load.push(Box::new(hook));
}
pub fn add_post_load_hook<F>(&mut self, hook: F)
where
F: Fn(&ModuleId, &LoadedModule) -> Result<()> + 'static,
{
self.hooks.post_load.push(Box::new(hook));
}
pub fn add_pre_unload_hook<F>(&mut self, hook: F)
where
F: Fn(&ModuleId) -> Result<()> + 'static,
{
self.hooks.pre_unload.push(Box::new(hook));
}
pub fn add_post_unload_hook<F>(&mut self, hook: F)
where
F: Fn(&ModuleId) -> Result<()> + 'static,
{
self.hooks.post_unload.push(Box::new(hook));
}
}
impl LoadedModule {
pub fn age(&self) -> Option<std::time::Duration> {
SystemTime::now().duration_since(self.loaded_at).ok()
}
pub fn has_dependencies(&self) -> bool {
!self.dependencies.is_empty()
}
pub fn has_dependents(&self) -> bool {
!self.dependents.is_empty()
}
}
impl Default for ModuleHooks {
fn default() -> Self {
Self::new()
}
}
impl ModuleHooks {
pub fn new() -> Self {
Self {
pre_load: Vec::new(),
post_load: Vec::new(),
pre_unload: Vec::new(),
post_unload: Vec::new(),
}
}
pub fn clear(&mut self) {
self.pre_load.clear();
self.post_load.clear();
self.pre_unload.clear();
self.post_unload.clear();
}
pub fn hook_count(&self) -> usize {
self.pre_load.len() + self.post_load.len() + self.pre_unload.len() + self.post_unload.len()
}
}
impl Default for ModuleManager {
fn default() -> Self {
Self::new().expect("Failed to create default ModuleManager")
}
}