tlua/
lua_functions.rs

1use std::ffi::CString;
2use std::io::Cursor;
3use std::io::Error as IoError;
4use std::io::Read;
5use std::num::NonZeroI32;
6use std::panic::Location;
7
8use crate::{
9    ffi, impl_object, nzi32,
10    object::{Call, CallError, FromObject, Object},
11    AsLua, LuaError, LuaRead, LuaState, Push, PushGuard, PushInto, PushOne, PushOneInto,
12};
13
14/// Wrapper around a `&str`. When pushed, the content will be parsed as Lua code and turned into a
15/// function.
16///
17/// Since pushing this value can fail in case of a parsing error, you must use the `checked_set`
18/// method instead of `set`.
19///
20/// > **Note**: This struct is a wrapper around `LuaCodeFromReader`. There's no advantage in using
21/// > it except that it is more convenient. More advanced usages (such as returning a Lua function
22/// > from a Rust function) can be done with `LuaCodeFromReader`.
23///
24/// # Example
25///
26/// ```no_run
27/// let lua = tlua::Lua::new();
28/// lua.checked_set("hello", &tlua::LuaCode("return 5")).unwrap();
29///
30/// let r: i32 = lua.eval("return hello();").unwrap();
31/// assert_eq!(r, 5);
32/// ```
33#[derive(Debug)]
34pub struct LuaCode<'a>(pub &'a str);
35
36impl<L> Push<L> for LuaCode<'_>
37where
38    L: AsLua,
39{
40    type Err = LuaError;
41
42    #[track_caller]
43    #[inline]
44    fn push_to_lua(&self, lua: L) -> Result<PushGuard<L>, (LuaError, L)> {
45        let reader = Cursor::new(self.0.as_bytes());
46        LuaCodeFromReader::new(reader).push_into_lua(lua)
47    }
48}
49
50impl<L> PushOne<L> for LuaCode<'_> where L: AsLua {}
51
52/// Wrapper around a `Read` object. When pushed, the content will be parsed as Lua code and turned
53/// into a function.
54///
55/// Since pushing this value can fail in case of a reading error or a parsing error, you must use
56/// the `checked_set` method instead of `set`.
57///
58/// # Example: returning a Lua function from a Rust function
59///
60/// ```no_run
61/// use std::io::Cursor;
62///
63/// let lua = tlua::Lua::new();
64///
65/// lua.set("call_rust", tlua::function0(|| -> tlua::LuaCodeFromReader<Cursor<String>> {
66///     let lua_code = "return 18;";
67///     return tlua::LuaCodeFromReader::new(Cursor::new(lua_code.to_owned()));
68/// }));
69///
70/// let r: i32 = lua.eval("local lua_func = call_rust(); return lua_func();").unwrap();
71/// assert_eq!(r, 18);
72/// ```
73#[derive(Debug)]
74pub struct LuaCodeFromReader<R> {
75    reader: R,
76    location: &'static Location<'static>,
77    chunkname: Option<String>,
78}
79
80impl<R> LuaCodeFromReader<R> {
81    #[track_caller]
82    pub fn new(reader: R) -> Self {
83        Self {
84            reader,
85            location: Location::caller(),
86            chunkname: None,
87        }
88    }
89
90    #[inline]
91    pub fn set_chunkname(&mut self, chunkname: String) {
92        self.chunkname = Some(chunkname);
93    }
94}
95
96impl<L, R> PushInto<L> for LuaCodeFromReader<R>
97where
98    L: AsLua,
99    R: Read,
100{
101    type Err = LuaError;
102
103    #[inline]
104    fn push_into_lua(self, lua: L) -> Result<PushGuard<L>, (LuaError, L)> {
105        unsafe {
106            struct ReadData<R> {
107                reader: R,
108                buffer: [u8; 128],
109                triggered_error: Option<IoError>,
110            }
111
112            let mut read_data = ReadData {
113                reader: self.reader,
114                buffer: [0; 128],
115                triggered_error: None,
116            };
117
118            extern "C" fn reader<R>(
119                _: LuaState,
120                data: *mut libc::c_void,
121                size: *mut libc::size_t,
122            ) -> *const libc::c_char
123            where
124                R: Read,
125            {
126                unsafe {
127                    let data: *mut ReadData<R> = data as *mut _;
128                    let data: &mut ReadData<R> = &mut *data;
129
130                    if data.triggered_error.is_some() {
131                        (*size) = 0;
132                        return data.buffer.as_ptr() as *const libc::c_char;
133                    }
134
135                    match data.reader.read(&mut data.buffer) {
136                        Ok(len) => (*size) = len as libc::size_t,
137                        Err(e) => {
138                            (*size) = 0;
139                            data.triggered_error = Some(e);
140                        }
141                    };
142
143                    data.buffer.as_ptr() as *const libc::c_char
144                }
145            }
146
147            let chunkname;
148            if let Some(name) = &self.chunkname {
149                chunkname = crate::util::into_cstring_lossy(name.into());
150            } else {
151                let location = format!("=[{}:{}]\0", self.location.file(), self.location.line());
152                chunkname = CString::from_vec_with_nul_unchecked(location.into());
153            }
154
155            let (load_return_value, pushed_value) = {
156                let code = ffi::lua_load(
157                    lua.as_lua(),
158                    reader::<R>,
159                    &mut read_data as *mut ReadData<_> as *mut _,
160                    chunkname.as_ptr(),
161                );
162                (code, PushGuard::new(lua, 1))
163            };
164
165            if read_data.triggered_error.is_some() {
166                let error = read_data.triggered_error.unwrap();
167                return Err((LuaError::ReadError(error), pushed_value.into_inner()));
168            }
169
170            if load_return_value == 0 {
171                return Ok(pushed_value);
172            }
173
174            let error_msg: String = LuaRead::lua_read(pushed_value.as_lua())
175                .expect("can't find error message at the top of the Lua stack");
176
177            if load_return_value == ffi::LUA_ERRMEM {
178                panic!("LUA_ERRMEM");
179            }
180
181            if load_return_value == ffi::LUA_ERRSYNTAX {
182                return Err((LuaError::SyntaxError(error_msg), pushed_value.into_inner()));
183            }
184
185            panic!("Unknown error while calling lua_load");
186        }
187    }
188}
189
190impl<L, R> PushOneInto<L> for LuaCodeFromReader<R>
191where
192    L: AsLua,
193    R: Read,
194{
195}
196
197/// Handle to a function in the Lua context.
198///
199/// Just like you can read variables as integers and strings, you can also read Lua functions by
200/// requesting a `LuaFunction` object. Once you have a `LuaFunction` you can call it with `call()`.
201///
202/// > **Note**: Passing parameters when calling the function is not yet implemented.
203///
204/// # Example
205///
206/// ```no_run
207/// let lua = tlua::Lua::new();
208/// lua.exec("function foo() return 12 end").unwrap();
209///
210/// let foo: tlua::LuaFunction<_> = lua.get("foo").unwrap();
211/// let result: i32 = foo.call().unwrap();
212/// assert_eq!(result, 12);
213/// ```
214// TODO: example for how to get a LuaFunction as a parameter of a Rust function
215#[derive(Debug)]
216pub struct LuaFunction<L> {
217    inner: Object<L>,
218}
219
220impl<L> LuaFunction<L>
221where
222    L: AsLua,
223{
224    unsafe fn new(lua: L, index: NonZeroI32) -> Self {
225        Self::from_obj(Object::new(lua, index))
226    }
227
228    pub fn into_inner(self) -> L {
229        self.inner.into_guard()
230    }
231}
232
233impl_object! { LuaFunction,
234    check(lua, index) {
235        ffi::lua_isfunction(lua.as_lua(), index.into())
236    }
237    impl Call,
238}
239
240impl<'lua, L> LuaFunction<L>
241where
242    L: 'lua,
243    L: AsLua,
244{
245    /// Calls the function. Doesn't allow passing parameters.
246    ///
247    /// TODO: will eventually disappear and get replaced with `call_with_args`
248    ///
249    /// Returns an error if there is an error while executing the Lua code (eg. a function call
250    /// returns an error), or if the requested return type doesn't match the actual return type.
251    ///
252    /// > **Note**: In order to pass parameters, see `call_with_args` instead.
253    #[track_caller]
254    #[inline]
255    pub fn call<V>(&'lua self) -> Result<V, LuaError>
256    where
257        V: LuaRead<PushGuard<&'lua L>>,
258    {
259        Call::call(self)
260    }
261
262    /// Calls the function taking ownership of the underlying push guard.
263    /// Doesn't allow passing parameters.
264    ///
265    /// TODO: will eventually disappear and get replaced with `call_with_args`
266    ///
267    /// Returns an error if there is an error while executing the Lua code (eg. a function call
268    /// returns an error), or if the requested return type doesn't match the actual return type.
269    ///
270    /// > **Note**: In order to pass parameters, see `into_call_with_args` instead.
271    #[track_caller]
272    #[inline]
273    pub fn into_call<V>(self) -> Result<V, LuaError>
274    where
275        V: LuaRead<PushGuard<Self>>,
276    {
277        Call::into_call(self)
278    }
279
280    /// Calls the function with parameters.
281    ///
282    /// TODO: should be eventually be renamed to `call`
283    ///
284    /// **Note:** this function can return multiple values if `V` is a tuple.
285    /// * If the expected number of values is less than the actual, only the
286    ///   first few values will be returned.
287    /// * If the expected number of values is greater than the actual, the
288    ///   function will return an error, unless the excess elements are
289    ///   `Option<T>`.
290    ///
291    /// You can either pass a single value by passing a single value, or multiple parameters by
292    /// passing a tuple.
293    /// If you pass a tuple, the first element of the tuple will be the first argument, the second
294    /// element of the tuple the second argument, and so on.
295    ///
296    /// Returns an error if there is an error while executing the Lua code (eg. a function call
297    /// returns an error), if the requested return type doesn't match the actual return type, or
298    /// if we failed to push an argument.
299    ///
300    /// # Example
301    ///
302    /// ```no_run
303    /// let lua = tlua::Lua::new();
304    /// lua.exec("function sub(a, b) return a - b end").unwrap();
305    ///
306    /// let foo: tlua::LuaFunction<_> = lua.get("sub").unwrap();
307    /// let result: i32 = foo.call_with_args((18, 4)).unwrap();
308    /// assert_eq!(result, 14);
309    /// ```
310    ///
311    /// # Multiple return values
312    ///
313    /// ```no_run
314    /// let lua = tlua::Lua::new();
315    /// lua.exec("function divmod(a, b) return math.floor(a / b), a % b end").unwrap();
316    ///
317    /// let foo: tlua::LuaFunction<_> = lua.get("divmod").unwrap();
318    ///
319    /// let first_result: i32 = foo.call_with_args((18, 4)).unwrap();
320    /// assert_eq!(first_result, 4);
321    ///
322    /// let all_result: (i32, i32) = foo.call_with_args((18, 4)).unwrap();
323    /// assert_eq!(all_result, (4, 2));
324    ///
325    /// let excess_results: (i32, i32, Option<i32>) = foo.call_with_args((18, 4)).unwrap();
326    /// assert_eq!(excess_results, (4, 2, None));
327    /// ```
328    #[track_caller]
329    #[inline]
330    pub fn call_with_args<V, A>(&'lua self, args: A) -> Result<V, CallError<A::Err>>
331    where
332        A: PushInto<LuaState>,
333        V: LuaRead<PushGuard<&'lua L>>,
334    {
335        Call::call_with(self, args)
336    }
337
338    /// Calls the function with parameters taking ownership of the underlying
339    /// push guard.
340    ///
341    /// TODO: should be eventually be renamed to `call`
342    ///
343    /// **Note:** this function can return multiple values if `V` is a tuple.
344    /// * If the expected number of values is less than the actual, only the
345    ///   first few values will be returned.
346    /// * If the expected number of values is greater than the actual, the
347    ///   function will return an error, unless the excess elements are
348    ///   `Option<T>`.
349    ///
350    /// You can either pass a single value by passing a single value, or multiple parameters by
351    /// passing a tuple.
352    /// If you pass a tuple, the first element of the tuple will be the first argument, the second
353    /// element of the tuple the second argument, and so on.
354    ///
355    /// Returns an error if there is an error while executing the Lua code (eg. a function call
356    /// returns an error), if the requested return type doesn't match the actual return type, or
357    /// if we failed to push an argument.
358    ///
359    /// # Example
360    ///
361    /// ```no_run
362    /// let lua = tlua::Lua::new();
363    /// lua.exec("function sub(a, b) return a - b end").unwrap();
364    ///
365    /// let foo: tlua::LuaFunction<_> = lua.get("sub").unwrap();
366    /// let result: i32 = foo.into_call_with_args((18, 4)).unwrap();
367    /// assert_eq!(result, 14);
368    /// ```
369    ///
370    /// # Multiple return values
371    ///
372    /// ```no_run
373    /// let lua = tlua::Lua::new();
374    /// lua.exec("function divmod(a, b) return math.floor(a / b), a % b end").unwrap();
375    ///
376    /// let foo: tlua::LuaFunction<_> = lua.get("divmod").unwrap();
377    ///
378    /// let all_result: (i32, i32) = foo.into_call_with_args((18, 4)).unwrap();
379    /// assert_eq!(all_result, (4, 2));
380    /// ```
381    #[track_caller]
382    #[inline]
383    pub fn into_call_with_args<V, A>(self, args: A) -> Result<V, CallError<A::Err>>
384    where
385        A: PushInto<LuaState>,
386        V: LuaRead<PushGuard<Self>>,
387    {
388        Call::into_call_with(self, args)
389    }
390}
391
392impl<L> LuaFunction<PushGuard<L>>
393where
394    L: AsLua,
395{
396    /// Builds a new `LuaFunction` from the code of a reader.
397    ///
398    /// Returns an error if reading from the `Read` object fails or if there is a syntax error in
399    /// the code.
400    ///
401    /// # Example
402    ///
403    /// ```no_run
404    /// use std::io::Cursor;
405    ///
406    /// let lua = tlua::Lua::new();
407    ///
408    /// let f = tlua::LuaFunction::load_from_reader(&lua, Cursor::new("return 8")).unwrap();
409    /// let ret: i32 = f.call().unwrap();
410    /// assert_eq!(ret, 8);
411    /// ```
412    #[track_caller]
413    #[inline]
414    pub fn load_from_reader(lua: L, code: impl Read) -> Result<Self, LuaError> {
415        match LuaCodeFromReader::new(code).push_into_lua(lua) {
416            Ok(pushed) => unsafe { Ok(Self::new(pushed, nzi32!(-1))) },
417            Err((err, _)) => Err(err),
418        }
419    }
420
421    /// Builds a new `LuaFunction` from a raw string.
422    ///
423    /// > **Note**: This is just a wrapper around `load_from_reader`. There is no advantage in
424    /// > using `load` except that it is more convenient.
425    // TODO: remove this function? it's only a thin wrapper and it's for a very niche situation
426    #[track_caller]
427    #[inline(always)]
428    pub fn load(lua: L, code: &str) -> Result<Self, LuaError> {
429        let reader = Cursor::new(code.as_bytes());
430        Self::load_from_reader(lua, reader)
431    }
432
433    /// Loads the lua `code` and returns it as a lua function.
434    /// The provided `chunkname` will be used for the error messages.
435    #[track_caller]
436    pub fn load_file_contents(lua: L, code: &str, chunkname: &str) -> Result<Self, LuaError> {
437        let reader = Cursor::new(code.as_bytes());
438        let mut reader = LuaCodeFromReader::new(reader);
439        reader.chunkname = Some(chunkname.into());
440        match reader.push_into_lua(lua) {
441            Ok(pushed) => unsafe { Ok(Self::new(pushed, nzi32!(-1))) },
442            Err((err, _)) => Err(err),
443        }
444    }
445}