1use std::{
7 os, panic,
8 ffi::CString,
9 ops::{Deref, Range},
10 slice,
11};
12
13use emacs_module::{emacs_value, EmacsSubr};
14
15use crate::{Env, Value, Result, FromLisp, IntoLisp};
16
17#[doc(hidden)]
18#[macro_export]
19macro_rules! __export_functions {
20 ($env:expr, $prefix:expr, $mappings:tt,) => {
22 $crate::__export_functions!($env, $prefix, $mappings)
23 };
24 ($env:expr, $prefix:expr, {
26 $( $name:expr => $declaration:tt ),+,
27 }) => {
28 $crate::__export_functions!($env, $prefix, {
29 $( $name => $declaration ),*
30 })
31 };
32 ($env:expr, $prefix:expr, {
34 $( $name:expr => $declaration:tt ),*
35 }) => {
36 {
37 use $crate::func::Manage;
38 $( $crate::__export_functions!(decl, $env, $prefix, $name, $declaration)?; )*
39 }
40 };
41
42 (decl, $env:expr, $prefix:expr, $name:expr, ($func:path, $( $opt:expr ),+,)) => {
44 $crate::__export_functions!(decl, $env, $prefix, $name, ($func, $( $opt ),*))
45 };
46 (decl, $env:expr, $prefix:expr, $name:expr, ($func:path, $( $opt:expr ),+)) => {
48 $env.fset(
49 &format!("{}{}", $prefix, $name),
50 $crate::lambda!($env, $func, $($opt),*)?
51 )
52 };
53}
54
55#[doc(hidden)]
57#[macro_export]
58macro_rules! lambda {
59 ($env:expr, $func:path, $arities:expr $(,)*) => {
61 $crate::lambda!($env, $func, $arities, "")
62 };
63
64 ($env:expr, $func:path, $arities:expr, $doc:expr $(,)*) => {
66 {
67 use $crate::func::HandleCall;
68 use $crate::func::Manage;
69 unsafe extern "C" fn extern_lambda(
71 env: *mut $crate::raw::emacs_env,
72 nargs: isize,
73 args: *mut $crate::raw::emacs_value,
74 _data: *mut ::std::os::raw::c_void,
75 ) -> $crate::raw::emacs_value {
76 let env = $crate::Env::new(env);
77 let env = $crate::CallEnv::new(env, nargs, args);
78 env.handle_call($func)
79 }
80
81 unsafe { $env.make_function(extern_lambda, $arities, $doc, ::std::ptr::null_mut()) }
83 }
84 };
85}
86
87#[deprecated(since = "0.7.0", note = "Please use `emacs::lambda!` instead")]
88#[doc(hidden)]
89#[macro_export]
90macro_rules! emacs_lambda {
91 ($($inner:tt)*) => {
92 $crate::lambda!($($inner)*)
93 };
94}
95
96pub trait Manage {
97 unsafe fn make_function<T: Into<Vec<u8>>>(
98 &self,
99 function: EmacsSubr,
100 arities: Range<usize>,
101 doc: T,
102 data: *mut os::raw::c_void,
103 ) -> Result<Value<'_>>;
104
105 fn fset(&self, name: &str, func: Value<'_>) -> Result<Value<'_>>;
106}
107
108impl Manage for Env {
109 #[allow(unused_unsafe)]
113 unsafe fn make_function<T: Into<Vec<u8>>>(
114 &self,
115 function: EmacsSubr,
116 arities: Range<usize>,
117 doc: T,
118 data: *mut os::raw::c_void,
119 ) -> Result<Value<'_>> {
120 unsafe_raw_call_value!(
121 self,
122 make_function,
123 arities.start as isize,
124 arities.end as isize,
125 Some(function),
126 CString::new(doc)?.as_ptr(),
127 data
128 )
129 }
130
131 fn fset(&self, name: &str, func: Value<'_>) -> Result<Value<'_>> {
132 let symbol = self.intern(name)?;
133 self.call("fset", [symbol, func])
134 }
135}
136
137#[doc(hidden)]
142#[derive(Debug)]
143pub struct CallEnv {
144 env: Env,
145 nargs: usize,
146 args: *mut emacs_value,
147}
148
149impl CallEnv {
151 #[doc(hidden)]
152 #[inline]
153 pub unsafe fn new(
154 env: Env,
155 nargs: isize,
156 args: *mut emacs_value,
157 ) -> Self {
158 let nargs = nargs as usize;
159 Self { env, nargs, args }
160 }
161
162 #[doc(hidden)]
163 #[inline]
164 pub fn raw_args(&self) -> &[emacs_value] {
165 unsafe { slice::from_raw_parts(self.args, self.nargs) }
167 }
168
169 pub fn args(&self) -> Vec<Value<'_>> {
170 self.raw_args().iter().map(|v| unsafe { Value::new(*v, &self.env) }).collect()
172 }
173
174 #[inline]
175 pub fn get_arg(&self, i: usize) -> Value<'_> {
176 let args: &[emacs_value] = self.raw_args();
177 unsafe { Value::new(args[i], &self) }
179 }
180
181 #[inline]
182 pub fn parse_arg<'e, T: FromLisp<'e>>(&'e self, i: usize) -> Result<T> {
183 self.get_arg(i).into_rust()
184 }
185}
186
187impl Deref for CallEnv {
189 type Target = Env;
190
191 #[doc(hidden)]
192 #[inline(always)]
193 fn deref(&self) -> &Env {
194 &self.env
195 }
196}
197
198pub trait HandleCall {
199 fn handle_call<'e, T, F>(&'e self, f: F) -> emacs_value
200 where
201 F: Fn(&'e CallEnv) -> Result<T> + panic::RefUnwindSafe,
202 T: IntoLisp<'e>;
203}
204
205impl HandleCall for CallEnv {
206 #[inline]
207 fn handle_call<'e, T, F>(&'e self, f: F) -> emacs_value
208 where
209 F: Fn(&'e CallEnv) -> Result<T> + panic::RefUnwindSafe,
210 T: IntoLisp<'e>,
211 {
212 let env = panic::AssertUnwindSafe(self);
213 let result = panic::catch_unwind(|| unsafe {
214 let rust_result = f(&env);
215 let lisp_result = rust_result.and_then(|t| t.into_lisp(&env));
216 env.maybe_exit(lisp_result)
217 });
218 env.handle_panic(result)
219 }
220}