use crate::{
Environment,
builtin::{DEFAULT_KSL_PATH, KSL_PATH_ENV},
expand_tilde,
value::Value,
};
pub(crate) fn apply_plugin(lib: &str, name: &str, args: &[Value], env: Environment) -> Result<Value, std::sync::Arc<str>> {
let path_with_ext = format!(
"{}{}.{}",
if cfg!(target_os = "windows") {
""
} else {
"lib"
},
lib,
if cfg!(target_os = "windows") {
"dll"
} else if cfg!(target_os = "macos") {
"dylib"
} else {
"so"
}
);
let real_path = if let Ok(true) = std::fs::exists(&path_with_ext) {
path_with_ext
} else {
let path_prepend_lib = format!("lib/{path_with_ext}");
if let Ok(true) = std::fs::exists(&path_prepend_lib) {
path_prepend_lib
} else {
let ksl_path = match std::env::var(KSL_PATH_ENV) {
Ok(p) => std::sync::Arc::from(p),
Err(_) => DEFAULT_KSL_PATH.clone(),
};
match expand_tilde(ksl_path) {
Ok(p) => p.join("lib").join(&path_with_ext).display().to_string(),
Err(_) => path_with_ext,
}
}
};
static LOADED_LIBS: std::sync::LazyLock<
parking_lot::Mutex<rustc_hash::FxHashMap<String, std::sync::Arc<libloading::Library>>>,
> = std::sync::LazyLock::new(|| parking_lot::Mutex::new(rustc_hash::FxHashMap::default()));
let library_arc = {
let mut cache = LOADED_LIBS.lock();
if let Some(l) = cache.get(&real_path) {
l.clone()
} else {
let lib_obj = unsafe { libloading::Library::new(&real_path) }.map_err(|e| {
std::sync::Arc::from(format!(
"Error[ksl::eval::plugin]: Failed to load library `{}`: {}",
real_path, e
))
})?;
let arc = std::sync::Arc::new(lib_obj);
cache.insert(real_path.clone(), arc.clone());
arc
}
};
unsafe {
match library_arc.get::<fn(&[Value], Environment) -> Result<Value, std::sync::Arc<str>>>(name.as_bytes()) {
Ok(func) => func(args, env),
Err(e) => Err(std::sync::Arc::from(format!(
"Error[ksl::eval::plugin]: Symbol `{}` not found in `{}`: {}",
name, lib, e
))),
}
}
}