ksl 0.1.30

KSL core library and interpreter
Documentation
// ksl::eval::plugin

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,
            }
        }
    };
    // loading libraries
    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
        }
    };
    // call plugin function from library
    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
            ))),
        }
    }
}