use std::ops::Deref;
use once_cell::sync::OnceCell;
use emacs_module::emacs_value;
use super::*;
#[derive(Debug)]
#[repr(transparent)]
pub struct GlobalRef {
raw: emacs_value
}
impl GlobalRef {
pub fn new(value: Value) -> Self {
let env = value.env;
let raw = unsafe_raw_call_no_exit!(env, make_global_ref, value.raw);
Self { raw }
}
pub(crate) unsafe fn from_raw(raw: emacs_value) -> Self {
Self { raw }
}
pub fn free(self, env: &Env) -> Result<()> {
unsafe_raw_call!(env, free_global_ref, self.raw)?;
Ok(())
}
#[inline]
pub fn bind<'e, 'g: 'e>(&'g self, env: &'e Env) -> Value<'e> {
unsafe { Value::new(self.raw, env) }
}
pub fn clone(&self, env: &Env) -> Self {
self.bind(env).make_global_ref()
}
}
unsafe impl Send for GlobalRef {}
unsafe impl Sync for GlobalRef {}
impl<'e> FromLisp<'e> for GlobalRef {
#[inline(always)]
fn from_lisp(value: Value<'e>) -> Result<Self> {
Ok(Self::new(value))
}
}
impl<'e> IntoLisp<'e> for &'e GlobalRef {
#[inline(always)]
fn into_lisp(self, env: &'e Env) -> Result<Value<'e>> {
Ok(self.bind(env))
}
}
impl<'e> Value<'e> {
#[inline(always)]
pub fn make_global_ref(self) -> GlobalRef {
GlobalRef::new(self)
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! global_refs {
($($name:ident)*) => {
$(
#[allow(non_upper_case_globals)]
pub static $name: &'static $crate::OnceGlobalRef = {
static X: $crate::OnceGlobalRef = $crate::OnceGlobalRef::new();
&X
};
)*
};
($registrator_name:ident ($init_method:ident) =>
$(
$name:ident $( => $lisp_name:expr )?
)*
) => {
$crate::global_refs! {
$($name)*
}
#[$crate::deps::ctor::ctor]
fn $registrator_name() {
$crate::init::__GLOBAL_REFS__.try_lock()
.expect("Failed to acquire a write lock on the list of initializers for global refs")
.push(::std::boxed::Box::new(|env| {
$(
#[allow(unused_variables)]
let name = $crate::deps::emacs_macros::lisp_name!($name);
$( let name = $lisp_name; )?
$crate::OnceGlobalRef::$init_method(&$name, env, name)?;
)*
Ok(())
}));
}
};
}
#[derive(Debug)]
#[repr(transparent)]
pub struct OnceGlobalRef {
inner: OnceCell<GlobalRef>
}
impl OnceGlobalRef {
pub const fn new() -> Self {
Self { inner: OnceCell::new() }
}
#[doc(hidden)]
pub fn init<F: FnOnce(&Env) -> Result<Value>>(&self, env: &Env, f: F) -> Result<&GlobalRef> {
let g = f(env)?.make_global_ref();
self.inner.set(g).expect("Cannot initialize a global reference more than once");
Ok(self.inner.get().expect("Failed to get an initialized OnceGlobalRef"))
}
#[doc(hidden)]
pub fn init_to_symbol(&self, env: &Env, name: &str) -> Result<&GlobalRef> {
self.init(env, |env| env.intern(name))
}
#[doc(hidden)]
pub fn init_to_function(&self, env: &Env, name: &str) -> Result<&GlobalRef> {
self.init(env, |env| {
let symbol = env.intern(name)?;
env.call("indirect-function", [symbol])
})
}
}
impl<'e> IntoLisp<'e> for &'e OnceGlobalRef {
#[inline(always)]
fn into_lisp(self, env: &'e Env) -> Result<Value<'e>> {
Ok(self.bind(env))
}
}
impl Deref for OnceGlobalRef {
type Target = GlobalRef;
#[inline]
fn deref(&self) -> &Self::Target {
self.inner.get().expect("Cannot access an uninitialized global reference")
}
}