use std::borrow::Borrow; use std::fmt; use std::hash::Hash; use std::path::Path; use std::sync::Arc; use erg_common::config::ErgConfig; use erg_common::dict::Dict; use erg_common::levenshtein::get_similar_name; use erg_common::pathutil::NormalizedPathBuf; use erg_common::shared::{ MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLockReadGuard, RwLockWriteGuard, Shared, }; use erg_common::Str; use crate::context::ModuleContext; use crate::hir::HIR; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ModId(usize); impl ModId { pub const fn new(id: usize) -> Self { Self(id) } pub const fn builtin() -> Self { Self(0) } pub const fn main() -> Self { Self(1) } } #[derive(Debug, Clone)] pub struct ModuleEntry { pub id: ModId, // builtin == 0, __main__ == 1 pub hir: Option, pub module: Arc, } impl fmt::Display for ModuleEntry { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "ModuleEntry(id = {}, name = {})", self.id.0, self.module.context.name ) } } impl ModuleEntry { pub fn new(id: ModId, hir: Option, ctx: ModuleContext) -> Self { Self { id, hir, module: Arc::new(ctx), } } pub fn builtin(ctx: ModuleContext) -> Self { Self { id: ModId::builtin(), hir: None, module: Arc::new(ctx), } } pub fn cfg(&self) -> &ErgConfig { &self.module.context.cfg } } /// Caches checked modules. /// In addition to being queried here when re-imported, it is also used when linking /// (Erg links all scripts defined in erg and outputs them to a single pyc file). #[derive(Debug, Default)] pub struct ModuleCache { cache: Dict, last_id: usize, } impl fmt::Display for ModuleCache { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ModuleCache {{")?; for (path, entry) in self.cache.iter() { writeln!(f, "{}: {}, ", path.display(), entry)?; } write!(f, "}}") } } impl ModuleCache { pub fn new() -> Self { Self { cache: Dict::new(), last_id: 0, } } pub fn get(&self, path: &P) -> Option<&ModuleEntry> where NormalizedPathBuf: Borrow

, { self.cache.get(path) } pub fn get_mut(&mut self, path: &Q) -> Option<&mut ModuleEntry> where NormalizedPathBuf: Borrow, { self.cache.get_mut(path) } pub fn register(&mut self, path: NormalizedPathBuf, hir: Option, ctx: ModuleContext) { self.last_id += 1; let id = ModId::new(self.last_id); let entry = ModuleEntry::new(id, hir, ctx); self.cache.insert(path, entry); } pub fn remove(&mut self, path: &Q) -> Option where NormalizedPathBuf: Borrow, { self.cache.remove(path) } pub fn remove_by_id(&mut self, id: ModId) -> Option { if let Some(name) = self.cache.iter().find_map(|(name, ent)| { if ent.id == id { Some(name.clone()) } else { None } }) { self.remove(&name) } else { None } } pub fn get_similar_name(&self, name: &str) -> Option { get_similar_name(self.cache.iter().map(|(v, _)| v.to_str().unwrap()), name).map(Str::rc) } pub fn rename_path(&mut self, old: &Path, new: NormalizedPathBuf) { if let Some(entry) = self.cache.remove(old) { self.cache.insert(new, entry); } } pub fn iter(&self) -> impl Iterator { self.cache.iter() } pub fn clear(&mut self) { self.cache.clear(); } } #[derive(Debug, Clone, Default)] pub struct SharedModuleCache(Shared); impl fmt::Display for SharedModuleCache { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Shared{}", self.0) } } impl SharedModuleCache { pub fn new() -> Self { Self(Shared::new(ModuleCache::new())) } pub fn is_empty(&self) -> bool { self.0.borrow().cache.is_empty() } pub fn len(&self) -> usize { self.0.borrow().cache.len() } pub fn get(&self, path: &Q) -> Option> where NormalizedPathBuf: Borrow, { if self.0.borrow().get(path).is_some() { Some(RwLockReadGuard::map(self.0.borrow(), |cache| { cache.get(path).unwrap() })) } else { None } } pub fn get_mut( &self, path: &Q, ) -> Option> where NormalizedPathBuf: Borrow, { if self.0.borrow().get(path).is_some() { Some(RwLockWriteGuard::map(self.0.borrow_mut(), |cache| { cache.get_mut(path).unwrap() })) } else { None } } pub fn get_ctx(&self, path: &Q) -> Option> where NormalizedPathBuf: Borrow, { self.0.borrow().get(path).map(|entry| entry.module.clone()) } pub fn ref_ctx( &self, path: &Q, ) -> Option> where NormalizedPathBuf: Borrow, { if self.0.borrow().get(path).is_some() { Some(RwLockReadGuard::map(self.0.borrow(), |cache| { cache.get(path).unwrap().module.as_ref() })) } else { None } } /// FIXME: see the comment in this function pub fn raw_ref_ctx(&self, path: &Q) -> Option<&ModuleContext> where NormalizedPathBuf: Borrow, { // Check if the module can be borrowed // If you delete `_ref`, this function returns `None` even if the key exists in rare cases let _ref = self.0.borrow(); let ref_ = unsafe { self.0.as_ptr().as_ref().unwrap() }; ref_.get(path).map(|entry| entry.module.as_ref()) } pub fn register>( &self, path: P, hir: Option, ctx: ModuleContext, ) { self.0.borrow_mut().register(path.into(), hir, ctx); } pub fn remove(&self, path: &Q) -> Option where NormalizedPathBuf: Borrow, { self.0.borrow_mut().remove(path) } pub fn remove_by_id(&self, id: ModId) -> Option { self.0.borrow_mut().remove_by_id(id) } pub fn get_similar_name(&self, name: &str) -> Option { self.0.borrow().get_similar_name(name) } pub fn initialize(&self) { let builtin_path = NormalizedPathBuf::from(""); let Some(builtin) = self.remove(&builtin_path) else { return; }; self.0.borrow_mut().clear(); self.register(builtin_path, None, Arc::try_unwrap(builtin.module).unwrap()); } pub fn rename_path>(&self, path: &Path, new: P) { self.0.borrow_mut().rename_path(path, new.into()); } pub fn ref_inner(&self) -> MappedRwLockReadGuard> { RwLockReadGuard::map(self.0.borrow(), |mc| &mc.cache) } }