use std::{
os, panic,
collections::HashMap,
sync::{Mutex, atomic::AtomicBool},
};
use once_cell::sync::Lazy;
use crate::{Env, Value, Result, ErrorKind};
#[doc(hidden)]
#[macro_export]
macro_rules! __module_init {
($init:ident) => {
#[no_mangle]
pub unsafe extern "C" fn emacs_module_init(
runtime: *mut $crate::raw::emacs_runtime,
) -> ::std::os::raw::c_int {
$crate::init::initialize(&$crate::Env::from_runtime(runtime), $init)
}
#[no_mangle]
pub unsafe extern "C" fn emacs_rs_module_init(
raw: *mut $crate::raw::emacs_env,
) -> ::std::os::raw::c_int {
$crate::init::initialize(&$crate::Env::new(raw), $init)
}
};
}
type InitFn = Box<dyn Fn(&Env) -> Result<()> + Send + 'static>;
type FnMap = HashMap<String, InitFn>;
pub static __GLOBAL_REFS__: Lazy<Mutex<Vec<InitFn>>> = Lazy::new(|| Mutex::new(vec![]));
pub static __CUSTOM_ERRORS__: Lazy<Mutex<Vec<InitFn>>> = Lazy::new(|| Mutex::new(vec![]));
pub static __INIT_FNS__: Lazy<Mutex<FnMap>> = Lazy::new(|| Mutex::new(HashMap::new()));
pub static __PREFIX__: Lazy<Mutex<[String; 2]>> = Lazy::new(|| Mutex::new(["".to_owned(), "-".to_owned()]));
pub static __MOD_IN_NAME__: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(true));
fn debugging() -> bool {
std::env::var("EMACS_MODULE_RS_DEBUG").unwrap_or_default() == "1"
}
fn check_gc_bug_31238(env: &Env) -> Result<()> {
let version = env.call("default-value", [env.intern("emacs-version")?])?;
let fixed = env.call("version<=", ("27", version))?.is_not_nil();
if debugging() {
env.call("set", (
env.intern("module-rs-disable-gc-bug-31238-workaround")?, fixed
))?;
}
crate::env::HAS_FIXED_GC_BUG_31238.get_or_init(|| fixed);
Ok(())
}
#[inline]
pub fn initialize<F>(env: &Env, init: F) -> os::raw::c_int
where
F: Fn(&Env) -> Result<Value<'_>> + panic::RefUnwindSafe,
{
let env = panic::AssertUnwindSafe(env);
let result = panic::catch_unwind(|| match (|| {
for init_global_ref in __GLOBAL_REFS__.try_lock()
.expect("Failed to acquire a read lock on the list of initializers for global-refs").iter() {
init_global_ref(&env)?;
}
env.define_core_errors()?;
check_gc_bug_31238(&env)?;
for define_error in __CUSTOM_ERRORS__.try_lock()
.expect("Failed to acquire a read lock on the list of initializers for custom error signals").iter() {
define_error(&env)?;
}
init(&env)
})() {
Ok(_) => 0,
Err(e) => {
if let Some(ErrorKind::Signal { symbol, data }) = e.downcast_ref::<ErrorKind>() {
env.call("message", (
"Error during initialization: symbol: %s data: %s",
unsafe { symbol.value(&env) },
unsafe { data.value(&env) },
))
} else {
env.message(format!("Error during initialization: {:#?}", e))
}
.expect("Failed to message Emacs about initialization error");
1
}
});
match result {
Ok(v) => v,
Err(e) => {
env.message(format!("Panic during initialization: {:#?}", e))
.expect("Failed to message Emacs about initialization panic");
2
}
}
}
fn lisp_name(s: &str) -> String {
s.replace("_", "-")
}
pub fn lisp_pkg(mod_path: &str) -> String {
let crate_name = mod_path.split("::").nth(0).expect("mod_path is empty!");
lisp_name(&crate_name)
}
pub fn lisp_path(mod_path: &str) -> String {
let split = mod_path.split("::");
let mut path =
__PREFIX__.try_lock().expect("Failed to acquire read lock of module prefix").join("");
for segment in split.skip(1) {
path.push_str(segment);
path.push('-');
}
lisp_name(&path)
}