Skip to main content

emacs/
value.rs

1use std::cell::{RefCell, Ref, RefMut};
2use std::cmp::PartialEq;
3
4use emacs_module::emacs_value;
5
6use crate::{subr, Env, Result, FromLisp, Transfer};
7
8/// A type that represents Lisp values.
9/// Values of this type can be copied around, but are lifetime-bound to the [`Env`] they come from.
10///
11/// They are also "proxy values" that are only useful when converted to Rust values, or used as
12/// arguments when calling back into the Lisp runtime.
13///
14/// [`Env`]: struct.Env.html
15#[derive(Debug, Clone, Copy)]
16pub struct Value<'e> {
17    pub(crate) raw: emacs_value,
18    pub env: &'e Env,
19}
20
21impl<'e> Value<'e> {
22    /// Constructs a new `Value`. Module code should not call this directly. It is public only for
23    /// some internal macros to use.
24    ///
25    /// # Safety
26    ///
27    /// The raw value must be valid for as long as the returned `Value` is used. This usually means
28    /// "as long as [`Env`] is alive", but can be shorter, depending on how the returned `Value` is
29    /// used at call site.
30    ///
31    /// [`Env`]: struct.Env.html
32    #[doc(hidden)]
33    pub unsafe fn new(raw: emacs_value, env: &'e Env) -> Self {
34        Self { raw, env }
35    }
36
37    /// Protects this value by registering with its [`Env`], effectively "rooting" the underlying
38    /// Lisp object during the lifetime of the [`Env`].
39    ///
40    /// Module code should not call this directly. It is public only for certain internal macros and
41    /// functions to work around Emacs GC's [bug #31238], which caused [issue #2].
42    ///
43    /// [`Env`]: struct.Env.html
44    /// [bug #31238]: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=31238
45    /// [issue #2]: https://github.com/ubolonton/emacs-module-rs/issues/2
46    #[doc(hidden)]
47    #[inline]
48    pub fn protect(self) -> Self {
49        let Self { env, raw } = self;
50        if let Some(protected) = &env.protected {
51            protected.borrow_mut().push(unsafe_raw_call_no_exit!(env, make_global_ref, raw));
52        }
53        self
54    }
55
56    pub fn is_not_nil(&self) -> bool {
57        let env = self.env;
58        unsafe_raw_call_no_exit!(env, is_not_nil, self.raw)
59    }
60
61    #[deprecated(since = "0.20.0", note = "Please use `==` instead")]
62    #[allow(clippy::should_implement_trait)]
63    pub fn eq(&self, other: Value<'e>) -> bool {
64        let env = self.env;
65        // Safety: `other` has the same lifetime.
66        unsafe_raw_call_no_exit!(env, eq, self.raw, other.raw)
67    }
68
69    /// Converts this value into a Rust value of the given type.
70    #[inline(always)]
71    pub fn into_rust<T: FromLisp<'e>>(self) -> Result<T> {
72        FromLisp::from_lisp(self)
73    }
74
75    #[inline]
76    pub fn into_ref<T: 'static>(self) -> Result<Ref<'e, T>> {
77        let container: &RefCell<T> = self.into_rust()?;
78        // TODO: Use .borrow(), we want panics.
79        Ok(container.try_borrow()?)
80    }
81
82    #[inline]
83    pub fn into_ref_mut<T: 'static>(self) -> Result<RefMut<'e, T>> {
84        let container: &RefCell<T> = self.into_rust()?;
85        // TODO: Use .borrow_mut(), we want panics.
86        Ok(container.try_borrow_mut()?)
87    }
88
89    /// Returns a mutable reference to the underlying Rust data wrapped by this value.
90    ///
91    /// # Safety
92    ///
93    /// There are several ways this can go wrong:
94    ///
95    /// - Lisp code can pass the same object through 2 different values in an argument list.
96    /// - Rust code earlier in the call chain may have copied this value.
97    /// - Rust code later in the call chain may receive a copy of this value.
98    ///
99    /// In general, it is better to wrap Rust data in `RefCell`, `Mutex`, or `RwLock` guards, before
100    /// moving them to Lisp, and then only access them through these guards (which can be obtained
101    /// back through [`into_rust`]). This method is for squeezing out the last bit of performance in
102    /// very rare situations.
103    ///
104    /// [`into_rust`]: #method.into_rust
105    pub unsafe fn get_mut<T: Transfer>(&mut self) -> Result<&mut T> {
106        self.get_raw_pointer().map(|r| &mut *r)
107    }
108
109    pub fn car<T: FromLisp<'e>>(self) -> Result<T> {
110        self.env.call(subr::car, (self,))?.into_rust()
111    }
112
113    pub fn cdr<T: FromLisp<'e>>(self) -> Result<T> {
114        self.env.call(subr::cdr, (self,))?.into_rust()
115    }
116}
117
118impl<'e> PartialEq for Value<'e> {
119    fn eq(&self, other: &Self) -> bool {
120        unsafe_raw_call_no_exit!(self.env, eq, self.raw, other.raw)
121    }
122}