emacs_rs_module/
lib.rs

1use std::{
2    collections::HashMap,
3    sync::Mutex,
4};
5
6use lazy_static::lazy_static;
7
8use emacs::{defun, Env, Value, Result};
9use emacs::raw::emacs_env;
10
11use libloading::{Library, Symbol};
12
13emacs::plugin_is_GPL_compatible!();
14
15lazy_static! {
16    static ref LIBRARIES: Mutex<HashMap<String, Library>> = Mutex::new(HashMap::new());
17}
18
19const INIT_FROM_ENV: &str = "emacs_rs_module_init";
20
21macro_rules! message {
22    ($env:expr, $fmt:expr $(, $args:expr)*) => {
23        $env.message(format!($fmt $(, $args)*))
24    };
25}
26
27// This module should be loaded by Emacs's built-in `module-load`, so it cannot be reloaded.
28#[emacs::module(name = "rs-module", separator = "/")]
29fn init(env: &Env) -> Result<Value<'_>> {
30    message!(env, "[rs-module]: defined functions...")
31}
32
33/// Helper function that enables live-reloading of Emacs's dynamic module. To be reloadable, the
34/// module be loaded by this function (`rs-module/load` in ELisp) instead of Emacs'
35/// `module-load`. (Re)loading is achieved by calling `(rs-module/load "/path/to/module")`.
36#[defun]
37fn load(env: &Env, path: String) -> Result<Value<'_>> {
38    let mut libraries = LIBRARIES.lock()
39        .expect("Failed to acquire lock for module map");
40    // TODO: How about tracking by feature name?
41    match libraries.remove(&path) {
42        Some(l) => message!(env, "[{}]: unloaded {:?}...", path, l)?,
43        None => message!(env, "[{}]: not loaded yet", path)?,
44    };
45    message!(env, "[{}]: loading...", path)?;
46    let lib = Library::new(&path)?;
47    message!(env, "[{}]: initializing...", path)?;
48    unsafe {
49        let rs_init: Symbol<'_, unsafe extern fn(*mut emacs_env) -> u32> =
50            lib.get(INIT_FROM_ENV.as_bytes())?;
51        rs_init(env.raw());
52    }
53    libraries.insert(path.clone(), lib);
54    message!(env, "[{}]: loaded and initialized", path)
55}