use std::{
collections::{HashMap, HashSet},
sync::Arc,
};
use arcstr::Substr;
use parking_lot::Mutex;
use tracing::{trace, warn};
use crate::{
symbols::name::SymName,
syntax::cst::{tree::NodeHandle, view::View},
util::data::either::Either,
};
use super::{PkgHandle, Project, querry::Queryable};
pub struct Exports {
pub pkg: PkgHandle,
pub defs: HashMap<SymName, (PkgHandle, NodeHandle)>,
}
impl Queryable for Exports {
type Input = PkgHandle;
type Ctx = Mutex<HashSet<PkgHandle>>;
type Backref = PkgHandle;
fn compute(pkg: Self::Input, ctx: &Self::Ctx, proj: &Project) -> Arc<Self> {
trace!("computing exports for {}", proj.get_pkg(pkg));
if !ctx.lock().insert(pkg) {
warn!("Circular dependency detected while computing package exports");
return Arc::new(Exports {
pkg,
defs: Default::default(),
});
}
Arc::new(Self::compute_exports(proj, pkg))
}
fn invalidate(&self, input: &Self::Input, ctx: &Self::Ctx, _proj: &Project) {
ctx.lock().remove(input);
}
fn invalidate_backref(&self, backref: Self::Backref, proj: &Project) {
proj.exports.invalidate(proj, &backref);
proj.bindings.invalidate(proj, &backref);
}
}
impl Exports {
fn compute_globals(
proj: &Project,
pkg: PkgHandle,
) -> HashMap<SymName, (PkgHandle, NodeHandle)> {
let syntax = proj.syntax.get(proj, pkg);
let mut defs: HashMap<SymName, (PkgHandle, NodeHandle)> = HashMap::new();
for imported in &syntax.top.imports {
let exports = proj.exports.get_as(proj, *imported, pkg);
defs.extend(exports.defs.iter().map(|(k, v)| (k.clone(), *v)));
}
defs.extend(syntax.top.defs.iter().map(|(k, v)| (k.clone(), (pkg, *v))));
proj.get_pkg(pkg)
.name
.clone()
.map(|name| defs.insert(SymName::Pkg(name), (pkg, syntax.root().syntax().handle())));
defs
}
fn compute_exports(proj: &Project, pkg: PkgHandle) -> Exports {
let syntax = proj.syntax.get(proj, pkg);
let defs = Self::compute_globals(proj, pkg);
let mut exported = HashMap::new();
exported.extend(
defs.iter()
.filter(|(name, _)| matches!(name, SymName::Pkg(_)))
.map(|(k, v)| (k.clone(), *v)),
);
if syntax.top.exports.is_empty() {
exported.extend(syntax.top.defs.iter().map(|(k, v)| (k.clone(), (pkg, *v))));
return Exports {
pkg,
defs: exported,
};
}
for export in &syntax.top.exports {
match export {
Either::Left(exported_name) => {
defs.get_key_value(&SymName::Value(exported_name.clone()))
.map(|(k, v)| exported.insert(k.clone(), *v));
defs.get_key_value(&SymName::Type(exported_name.clone()))
.map(|(k, v)| exported.insert(k.clone(), *v));
}
Either::Right(exported_pkg) => {
exported.extend(
defs.iter()
.filter(|(_, (def_pkg, _))| def_pkg == exported_pkg)
.map(|(k, v)| (k.clone(), *v)),
);
}
}
}
Exports {
pkg,
defs: exported,
}
}
pub fn lookup_package(&self, name: Substr) -> Option<PkgHandle> {
self.defs.get(&SymName::Pkg(name)).map(|(p, _)| *p)
}
}