use crate::interned_stdlib::INTERNED_STL;
use crate::prelude::*;
use crate::state::Directory;
use std::fs;
use std::path::{Path, PathBuf};
fn import(state: &mut State, args: &[Argument], glob_import: bool) -> Result<Atom> {
let name = args[0].variable(
"`import` argument must be a variable, string syntax was removed",
state,
)?;
if !name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') {
raise!(
state,
"Import",
"invalid characters in import name `{name}`, only a-Z, 0-9 and _ are allowed",
);
}
let mut import_state = State::new();
import_state.import_stack.clone_from(&state.import_stack);
for (global_ident, global_value) in state.storage.all_globals() {
import_state.storage.add_global(global_ident, global_value);
}
if let Directory::Regular(dir_path) = &state.file_directory
&& let Some(path) = try_resolve_import_in_dir(state, name, dir_path)?
{
if import_state.import_stack.contains(&path) {
raise!(
state,
"Import",
"cyclic import of `{name}` at path `{}` detected",
path.display()
);
}
import_state = import_state.with_source_file(&path).unwrap();
import_state.import_stack.push(path);
} else if let Some(code) = INTERNED_STL.get(name) {
import_state = import_state.with_code(code);
import_state.set_current_file_path(format!("<stl:{name}>"));
} else {
raise!(
state,
"Import",
"failed to find file for importing `{name}`"
);
}
let atom = import_state.run();
if let Some(exit_unwind_value) = import_state.exit_unwind_value {
state.exit_unwind_value = Some(exit_unwind_value);
return Ok(Atom::Null);
}
atom?;
state.storage.extend_from(
import_state.storage,
if glob_import {
None
} else {
Some(format!("{name}."))
},
);
Ok(Atom::Null)
}
fn try_resolve_import_in_dir(
state: &State,
name: &str,
dir_path: &Path,
) -> Result<Option<PathBuf>> {
let paths = fs::read_dir(dir_path)
.map_err(|err| {
state.raise(
"Import",
format!(
"error when reading directory `{}`: {err}",
dir_path.display()
),
)
})?
.flatten();
for item in paths {
if *item.file_name() == *format!("{name}.{FILE_EXTENSION}") {
return Ok(Some(item.path()));
}
}
Ok(None)
}
functions! {
"import"(1) => |state, args| import(state, args, true)
"__wip_module"(1) => |state, args| import(state, args, false)
}