use crate::parser::interpreter::eval::eval_expression; use crate::parser::tokens::tokenize_pick_args;
use crate::parser::interpreter::Interpreter;
use crate::parser::types::Value;
use libloading::{Library, Symbol};
use std::env::consts::{DLL_PREFIX, DLL_SUFFIX};
use std::ffi::c_void;
use std::path::{Path, PathBuf};
pub fn handle_extend_call(interp: &mut Interpreter, expr: &str) -> Result<Value, String> {
let inside = &expr["extend(".len()..expr.len() - 1].trim();
let parts = tokenize_pick_args(inside, interp.is_verbose())?;
if parts.is_empty() {
return Err("extend(...) requires at least one argument (the library name/path)".to_string());
}
if parts.len() > 2 {
return Err("extend(...) => at most 2 arguments: (libBaseName[, extraString])".to_string());
}
let so_path_val = eval_expression(interp, &parts[0])?;
let so_path_raw = match so_path_val {
Value::StrArray(ref ss) if ss.len() == 1 => ss[0].clone(),
_ => {
return Err(
"extend(...) => first arg must be a single string for the plugin name/path".to_string()
);
}
};
let maybe_extra: Option<String> = if parts.len() == 2 {
let val2 = eval_expression(interp, &parts[1])?;
match val2 {
Value::StrArray(ref ss) if ss.len() == 1 => Some(ss[0].clone()),
Value::IntArray(ref ia) if ia.len() == 1 => Some(ia[0].to_string()),
Value::Int(i) => Some(i.to_string()),
_ => None,
}
} else {
None
};
let user_path = Path::new(&so_path_raw);
let filename = match user_path.file_name() {
Some(osfn) => osfn.to_string_lossy().into_owned(),
None => return Err(format!("extend => cannot extract filename from '{}'", so_path_raw)),
};
let parent_dir = user_path.parent().unwrap_or_else(|| Path::new(""));
let base_str = filename
.trim_end_matches(".so")
.trim_end_matches(".dll")
.trim_end_matches(".dylib");
let actual_filename = format!("{}lava{}{}", DLL_PREFIX, base_str, DLL_SUFFIX);
let full_path: PathBuf = parent_dir.join(&actual_filename);
if interp.is_verbose() {
eprintln!("[extend] Attempting to load library => '{}'", full_path.display());
}
let old_names = interp
.get_dynamic_functions_for_clone()
.keys()
.map(|k| k.clone())
.collect::<Vec<_>>();
let lib_res = unsafe { Library::new(&full_path) };
let lib = match lib_res {
Ok(l) => l,
Err(e) => {
return Err(format!("extend => could not load '{}': {}", full_path.display(), e));
}
};
unsafe {
let maybe_init: std::result::Result<
Symbol<unsafe extern "C" fn(*mut c_void, *const c_void) -> i32>,
libloading::Error,
> = lib.get(b"Cargo_lock");
if let Ok(init_fn) = maybe_init {
let interp_ptr = interp as *mut Interpreter as *mut c_void;
let extra_ptr: *const c_void = if let Some(ref extra_str) = maybe_extra {
extra_str.as_ptr() as *const c_void
} else {
std::ptr::null()
};
if interp.is_verbose() {
eprintln!("[extend] Found Cargo_lock => calling it now...");
}
let rc = init_fn(interp_ptr, extra_ptr);
if rc != 0 {
return Err(format!(
"extend => plugin '{}' returned error code {} from Cargo_lock()",
full_path.display(),
rc
));
}
if interp.is_verbose() {
eprintln!(
"[extend] plugin '{}' => Cargo_lock() success. Extra arg = {:?}",
full_path.display(),
maybe_extra
);
}
} else if interp.is_verbose() {
eprintln!(
"[extend] plugin '{}' => no Cargo_lock symbol found. Kept loaded.",
full_path.display()
);
}
}
interp.add_library_handle(lib);
let new_set = interp
.get_dynamic_functions_for_clone()
.keys()
.map(|k| k.clone())
.collect::<Vec<_>>();
let mut newly_imported = Vec::new();
for nm in new_set {
if !old_names.contains(&nm) {
newly_imported.push(nm);
}
}
newly_imported.sort();
Ok(Value::StrArray(newly_imported))
}