1use std::sync::Arc;
4
5use crate::env::{Env, GlobalEnv, RequireRefer, RequireSpec};
6use crate::error::{EvalError, EvalResult};
7
8pub fn load_ns(globals: Arc<GlobalEnv>, spec: &RequireSpec, current_ns: &str) -> EvalResult<()> {
14 let ns_name = &spec.ns;
15
16 let already_loaded = globals.is_loaded(ns_name);
18 if !already_loaded {
19 {
21 let mut loading = globals.loading.lock().unwrap();
22 if loading.contains(ns_name.as_ref()) {
23 return Err(EvalError::Runtime(format!("circular require: {ns_name}")));
24 }
25 loading.insert(ns_name.clone());
26 }
27
28 let rel_path = ns_name.replace('.', "/").replace('-', "_");
31 let src_paths = globals.source_paths.read().unwrap().clone();
32 let (src, file_path): (String, String) =
33 if let Some(builtin) = globals.builtin_source(ns_name) {
34 (builtin.to_owned(), format!("<builtin:{ns_name}>"))
35 } else {
36 find_source_file(&rel_path, &src_paths).ok_or_else(|| {
37 EvalError::Runtime(format!("Could not find namespace {ns_name} on source path"))
38 })?
39 };
40
41 if ns_name.as_ref() != "clojure.core" {
43 globals.refer_all(ns_name, "clojure.core");
44 }
45
46 let saved_ns = globals
49 .lookup_var("clojure.core", "*ns*")
50 .and_then(|v| crate::dynamics::deref_var(&v));
51 {
52 let mut env = Env::new(globals.clone(), ns_name);
53 let mut parser = cljrs_reader::Parser::new(src, file_path);
54 let forms = parser.parse_all().map_err(EvalError::Read)?;
55 for form in forms {
56 let _alloc_frame = cljrs_gc::push_alloc_frame();
60 (*globals)
61 .eval(&form, &mut env)
62 .map_err(|e| annotate(e, ns_name))?;
63 }
64 }
65 if let Some(saved) = saved_ns
67 && let Some(var) = globals.lookup_var("clojure.core", "*ns*")
68 {
69 var.get().bind(saved);
70 }
71
72 globals.loading.lock().unwrap().remove(ns_name.as_ref());
74 globals.mark_loaded(ns_name);
75 }
76
77 if let Some(alias) = &spec.alias {
79 globals.add_alias(current_ns, alias, ns_name);
80 }
81
82 match &spec.refer {
84 RequireRefer::None => {}
85 RequireRefer::All => globals.refer_all(current_ns, ns_name),
86 RequireRefer::Named(names) => globals.refer_named(current_ns, ns_name, names),
87 }
88
89 Ok(())
90}
91
92fn find_source_file(rel: &str, src_paths: &[std::path::PathBuf]) -> Option<(String, String)> {
93 for dir in src_paths {
94 for ext in &[".cljrs", ".cljc"] {
95 let path = dir.join(format!("{rel}{ext}"));
96 if path.exists() {
97 let src = std::fs::read_to_string(&path).ok()?;
98 return Some((src, path.display().to_string()));
99 }
100 }
101 }
102 None
103}
104
105fn annotate(e: EvalError, ns_name: &Arc<str>) -> EvalError {
109 match e {
110 EvalError::Read(_) => e,
112 EvalError::Recur(_) => e,
114 other => EvalError::Runtime(format!("in {ns_name}: {other}")),
116 }
117}