emacs_native_async/
to_lisp.rs

1use std::{
2    mem::{replace, take},
3    ptr::null_mut,
4    sync::{Arc, Mutex, RwLock},
5};
6
7use emacs::{Env, IntoLisp, Transfer, Value};
8use libc::c_void;
9
10pub struct ToLispConvert {
11    inner: ToLispConvertInner,
12}
13
14enum ToLispConvertInner {
15    Unit,
16    I64(i64),
17    ISize(isize),
18    U64(u64),
19    USize(usize),
20    Bool(bool),
21    F64(f64),
22    Str(&'static str),
23    String(String),
24    Ptr(Option<unsafe extern "C" fn(arg1: *mut c_void)>, *mut c_void),
25    Lazy(Option<Box<dyn Send + Sync + FnOnce(&Env) -> emacs::Result<Value> + Send + Sync>>),
26}
27
28unsafe impl Sync for ToLispConvertInner {}
29unsafe impl Send for ToLispConvertInner {}
30
31impl ToLispConvert {
32    pub fn to_value(mut self, env: &Env) -> emacs::Result<Value> {
33        match &mut self.inner {
34            ToLispConvertInner::Unit => ().into_lisp(env),
35            ToLispConvertInner::I64(v) => v.into_lisp(env),
36            ToLispConvertInner::ISize(v) => v.into_lisp(env),
37            ToLispConvertInner::U64(v) => v.into_lisp(env),
38            ToLispConvertInner::USize(v) => v.into_lisp(env),
39            ToLispConvertInner::Bool(v) => v.into_lisp(env),
40            ToLispConvertInner::F64(v) => v.into_lisp(env),
41            ToLispConvertInner::Str(v) => v.into_lisp(env),
42            ToLispConvertInner::String(v) => take(v).into_lisp(env),
43            ToLispConvertInner::Ptr(fin, val) => unsafe {
44                env.make_user_ptr(take(fin), replace(val, null_mut()))
45            },
46            ToLispConvertInner::Lazy(f) => match take(f) {
47                Some(f) => f(env),
48                None => Err(anyhow::anyhow!("empty value")),
49            },
50        }
51    }
52    pub fn lazy<F: Send + Sync + 'static + FnOnce(&Env) -> emacs::Result<Value> + Send + Sync>(
53        f: F,
54    ) -> ToLispConvert {
55        Self {
56            inner: ToLispConvertInner::Lazy(Some(Box::new(f))),
57        }
58    }
59}
60
61impl From<()> for ToLispConvert {
62    fn from(_: ()) -> Self {
63        Self {
64            inner: ToLispConvertInner::Unit,
65        }
66    }
67}
68impl From<i8> for ToLispConvert {
69    fn from(v: i8) -> Self {
70        ToLispConvert {
71            inner: ToLispConvertInner::I64(v.into()),
72        }
73    }
74}
75impl From<i16> for ToLispConvert {
76    fn from(v: i16) -> Self {
77        ToLispConvert {
78            inner: ToLispConvertInner::I64(v.into()),
79        }
80    }
81}
82impl From<i32> for ToLispConvert {
83    fn from(v: i32) -> Self {
84        ToLispConvert {
85            inner: ToLispConvertInner::I64(v.into()),
86        }
87    }
88}
89impl From<i64> for ToLispConvert {
90    fn from(v: i64) -> Self {
91        ToLispConvert {
92            inner: ToLispConvertInner::I64(v),
93        }
94    }
95}
96impl From<isize> for ToLispConvert {
97    fn from(v: isize) -> Self {
98        ToLispConvert {
99            inner: ToLispConvertInner::ISize(v),
100        }
101    }
102}
103impl From<u8> for ToLispConvert {
104    fn from(v: u8) -> Self {
105        ToLispConvert {
106            inner: ToLispConvertInner::U64(v.into()),
107        }
108    }
109}
110impl From<u16> for ToLispConvert {
111    fn from(v: u16) -> Self {
112        ToLispConvert {
113            inner: ToLispConvertInner::U64(v.into()),
114        }
115    }
116}
117impl From<u32> for ToLispConvert {
118    fn from(v: u32) -> Self {
119        ToLispConvert {
120            inner: ToLispConvertInner::U64(v.into()),
121        }
122    }
123}
124impl From<u64> for ToLispConvert {
125    fn from(v: u64) -> Self {
126        ToLispConvert {
127            inner: ToLispConvertInner::U64(v),
128        }
129    }
130}
131impl From<usize> for ToLispConvert {
132    fn from(v: usize) -> Self {
133        ToLispConvert {
134            inner: ToLispConvertInner::USize(v),
135        }
136    }
137}
138impl From<bool> for ToLispConvert {
139    fn from(v: bool) -> Self {
140        ToLispConvert {
141            inner: ToLispConvertInner::Bool(v),
142        }
143    }
144}
145impl From<f64> for ToLispConvert {
146    fn from(v: f64) -> Self {
147        ToLispConvert {
148            inner: ToLispConvertInner::F64(v),
149        }
150    }
151}
152impl From<&'static str> for ToLispConvert {
153    fn from(v: &'static str) -> Self {
154        ToLispConvert {
155            inner: ToLispConvertInner::Str(v),
156        }
157    }
158}
159impl From<String> for ToLispConvert {
160    fn from(v: String) -> Self {
161        ToLispConvert {
162            inner: ToLispConvertInner::String(v),
163        }
164    }
165}
166
167//copied from Emacs crate
168/// Finalizes an embedded pointer. This is called by the GC when it discards a `user-ptr`.
169///
170/// This function also serves as a form of runtime type tag, relying on Rust's mono-morphization.
171unsafe extern "C" fn finalize<T: Transfer>(ptr: *mut c_void) {
172    #[cfg(build = "debug")]
173    println!("Finalizing {:#?} {}", ptr, T::type_name());
174    drop(Box::from_raw(ptr as *mut T));
175}
176
177impl<T: Transfer + Send + Sync> From<Box<T>> for ToLispConvert {
178    fn from(v: Box<T>) -> Self {
179        ToLispConvert {
180            inner: ToLispConvertInner::Ptr(
181                Some(finalize::<T>),
182                std::boxed::Box::<T>::into_raw(v).cast(),
183            ),
184        }
185    }
186}
187
188impl<T: 'static+Send> From<Mutex<T>> for ToLispConvert {
189    fn from(v: Mutex<T>) -> Self {
190        Box::new(v).into()
191    }
192}
193impl<T: 'static+Send+Sync> From<RwLock<T>> for ToLispConvert {
194    fn from(v: RwLock<T>) -> Self {
195        Box::new(v).into()
196    }
197}
198impl<T: 'static+Send+Sync> From<Arc<T>> for ToLispConvert {
199    fn from(v: Arc<T>) -> Self {
200        Box::new(v).into()
201    }
202}
203
204impl<'e> IntoLisp<'e> for ToLispConvert {
205    fn into_lisp(self, env: &'e Env) -> emacs::Result<Value<'e>> {
206        self.to_value(env)
207    }
208}
209
210impl Drop for ToLispConvertInner {
211    fn drop(&mut self) {
212        match self {
213            &mut Self::Ptr(Some(fin), val) => unsafe { fin(val) },
214            _ => {}
215        }
216    }
217}
218
219#[cfg(test)]
220mod tests {
221    use super::ToLispConvert;
222
223    #[allow(unused)]
224    struct IsSendSync<T: Send + Sync + 'static> {
225        t: T,
226    }
227
228    #[allow(unused)]
229    fn test_is_send_sync(_: IsSendSync<ToLispConvert>) -> () {
230        return ();
231    }
232}