use std::sync::Arc;
use crate::env::{Env, GlobalEnv, RequireRefer, RequireSpec};
use crate::error::{EvalError, EvalResult};
pub fn load_ns(globals: Arc<GlobalEnv>, spec: &RequireSpec, current_ns: &str) -> EvalResult<()> {
let ns_name = &spec.ns;
let already_loaded = globals.is_loaded(ns_name);
if !already_loaded {
{
let mut loading = globals.loading.lock().unwrap();
if loading.contains(ns_name.as_ref()) {
return Err(EvalError::Runtime(format!("circular require: {ns_name}")));
}
loading.insert(ns_name.clone());
}
let rel_path = ns_name.replace('.', "/").replace('-', "_");
let src_paths = globals.source_paths.read().unwrap().clone();
let (src, file_path): (String, String) =
if let Some(builtin) = globals.builtin_source(ns_name) {
(builtin.to_owned(), format!("<builtin:{ns_name}>"))
} else {
find_source_file(&rel_path, &src_paths).ok_or_else(|| {
EvalError::Runtime(format!("Could not find namespace {ns_name} on source path"))
})?
};
if ns_name.as_ref() != "clojure.core" {
globals.refer_all(ns_name, "clojure.core");
}
let saved_ns = globals
.lookup_var("clojure.core", "*ns*")
.and_then(|v| crate::dynamics::deref_var(&v));
{
let mut env = Env::new(globals.clone(), ns_name);
let mut parser = cljrs_reader::Parser::new(src, file_path);
let forms = parser.parse_all().map_err(EvalError::Read)?;
for form in forms {
let _alloc_frame = cljrs_gc::push_alloc_frame();
(*globals)
.eval(&form, &mut env)
.map_err(|e| annotate(e, ns_name))?;
}
}
if let Some(saved) = saved_ns
&& let Some(var) = globals.lookup_var("clojure.core", "*ns*")
{
var.get().bind(saved);
}
globals.loading.lock().unwrap().remove(ns_name.as_ref());
globals.mark_loaded(ns_name);
}
if let Some(alias) = &spec.alias {
globals.add_alias(current_ns, alias, ns_name);
}
match &spec.refer {
RequireRefer::None => {}
RequireRefer::All => globals.refer_all(current_ns, ns_name),
RequireRefer::Named(names) => globals.refer_named(current_ns, ns_name, names),
}
Ok(())
}
fn find_source_file(rel: &str, src_paths: &[std::path::PathBuf]) -> Option<(String, String)> {
for dir in src_paths {
for ext in &[".cljrs", ".cljc"] {
let path = dir.join(format!("{rel}{ext}"));
if path.exists() {
let src = std::fs::read_to_string(&path).ok()?;
return Some((src, path.display().to_string()));
}
}
}
None
}
fn annotate(e: EvalError, ns_name: &Arc<str>) -> EvalError {
match e {
EvalError::Read(_) => e,
EvalError::Recur(_) => e,
other => EvalError::Runtime(format!("in {ns_name}: {other}")),
}
}