Skip to main content

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 let Some(error) = read_data.triggered_error {
166                return Err((LuaError::ReadError(error), pushed_value.into_inner()));
167            }
168
169            if load_return_value == 0 {
170                return Ok(pushed_value);
171            }
172
173            let error_msg: String = LuaRead::lua_read(pushed_value.as_lua())
174                .expect("can't find error message at the top of the Lua stack");
175
176            if load_return_value == ffi::LUA_ERRMEM {
177                panic!("LUA_ERRMEM");
178            }
179
180            if load_return_value == ffi::LUA_ERRSYNTAX {
181                return Err((LuaError::SyntaxError(error_msg), pushed_value.into_inner()));
182            }
183
184            panic!("Unknown error while calling lua_load");
185        }
186    }
187}
188
189impl<L, R> PushOneInto<L> for LuaCodeFromReader<R>
190where
191    L: AsLua,
192    R: Read,
193{
194}
195
196/// Handle to a function in the Lua context.
197///
198/// Just like you can read variables as integers and strings, you can also read Lua functions by
199/// requesting a `LuaFunction` object. Once you have a `LuaFunction` you can call it with `call()`.
200///
201/// > **Note**: Passing parameters when calling the function is not yet implemented.
202///
203/// # Example
204///
205/// ```no_run
206/// let lua = tlua::Lua::new();
207/// lua.exec("function foo() return 12 end").unwrap();
208///
209/// let foo: tlua::LuaFunction<_> = lua.get("foo").unwrap();
210/// let result: i32 = foo.call().unwrap();
211/// assert_eq!(result, 12);
212/// ```
213// TODO: example for how to get a LuaFunction as a parameter of a Rust function
214#[derive(Debug)]
215pub struct LuaFunction<L> {
216    inner: Object<L>,
217}
218
219impl<L> LuaFunction<L>
220where
221    L: AsLua,
222{
223    unsafe fn new(lua: L, index: NonZeroI32) -> Self {
224        Self::from_obj(Object::new(lua, index))
225    }
226
227    pub fn into_inner(self) -> L {
228        self.inner.into_guard()
229    }
230}
231
232impl_object! { LuaFunction,
233    check(lua, index) {
234        ffi::lua_isfunction(lua.as_lua(), index.into())
235    }
236    impl Call,
237}
238
239impl<'lua, L> LuaFunction<L>
240where
241    L: 'lua,
242    L: AsLua,
243{
244    /// Calls the function. Doesn't allow passing parameters.
245    ///
246    /// TODO: will eventually disappear and get replaced with `call_with_args`
247    ///
248    /// Returns an error if there is an error while executing the Lua code (eg. a function call
249    /// returns an error), or if the requested return type doesn't match the actual return type.
250    ///
251    /// > **Note**: In order to pass parameters, see `call_with_args` instead.
252    #[track_caller]
253    #[inline]
254    pub fn call<V>(&'lua self) -> Result<V, LuaError>
255    where
256        V: LuaRead<PushGuard<&'lua L>>,
257    {
258        Call::call(self)
259    }
260
261    /// Calls the function taking ownership of the underlying push guard.
262    /// Doesn't allow passing parameters.
263    ///
264    /// TODO: will eventually disappear and get replaced with `call_with_args`
265    ///
266    /// Returns an error if there is an error while executing the Lua code (eg. a function call
267    /// returns an error), or if the requested return type doesn't match the actual return type.
268    ///
269    /// > **Note**: In order to pass parameters, see `into_call_with_args` instead.
270    #[track_caller]
271    #[inline]
272    pub fn into_call<V>(self) -> Result<V, LuaError>
273    where
274        V: LuaRead<PushGuard<Self>>,
275    {
276        Call::into_call(self)
277    }
278
279    /// Calls the function with parameters.
280    ///
281    /// TODO: should be eventually be renamed to `call`
282    ///
283    /// **Note:** this function can return multiple values if `V` is a tuple.
284    /// * If the expected number of values is less than the actual, only the
285    ///   first few values will be returned.
286    /// * If the expected number of values is greater than the actual, the
287    ///   function will return an error, unless the excess elements are
288    ///   `Option<T>`.
289    ///
290    /// You can either pass a single value by passing a single value, or multiple parameters by
291    /// passing a tuple.
292    /// If you pass a tuple, the first element of the tuple will be the first argument, the second
293    /// element of the tuple the second argument, and so on.
294    ///
295    /// Returns an error if there is an error while executing the Lua code (eg. a function call
296    /// returns an error), if the requested return type doesn't match the actual return type, or
297    /// if we failed to push an argument.
298    ///
299    /// # Example
300    ///
301    /// ```no_run
302    /// let lua = tlua::Lua::new();
303    /// lua.exec("function sub(a, b) return a - b end").unwrap();
304    ///
305    /// let foo: tlua::LuaFunction<_> = lua.get("sub").unwrap();
306    /// let result: i32 = foo.call_with_args((18, 4)).unwrap();
307    /// assert_eq!(result, 14);
308    /// ```
309    ///
310    /// # Multiple return values
311    ///
312    /// ```no_run
313    /// let lua = tlua::Lua::new();
314    /// lua.exec("function divmod(a, b) return math.floor(a / b), a % b end").unwrap();
315    ///
316    /// let foo: tlua::LuaFunction<_> = lua.get("divmod").unwrap();
317    ///
318    /// let first_result: i32 = foo.call_with_args((18, 4)).unwrap();
319    /// assert_eq!(first_result, 4);
320    ///
321    /// let all_result: (i32, i32) = foo.call_with_args((18, 4)).unwrap();
322    /// assert_eq!(all_result, (4, 2));
323    ///
324    /// let excess_results: (i32, i32, Option<i32>) = foo.call_with_args((18, 4)).unwrap();
325    /// assert_eq!(excess_results, (4, 2, None));
326    /// ```
327    #[track_caller]
328    #[inline]
329    pub fn call_with_args<V, A>(&'lua self, args: A) -> Result<V, CallError<A::Err>>
330    where
331        A: PushInto<LuaState>,
332        V: LuaRead<PushGuard<&'lua L>>,
333    {
334        Call::call_with(self, args)
335    }
336
337    /// Calls the function with parameters taking ownership of the underlying
338    /// push guard.
339    ///
340    /// TODO: should be eventually be renamed to `call`
341    ///
342    /// **Note:** this function can return multiple values if `V` is a tuple.
343    /// * If the expected number of values is less than the actual, only the
344    ///   first few values will be returned.
345    /// * If the expected number of values is greater than the actual, the
346    ///   function will return an error, unless the excess elements are
347    ///   `Option<T>`.
348    ///
349    /// You can either pass a single value by passing a single value, or multiple parameters by
350    /// passing a tuple.
351    /// If you pass a tuple, the first element of the tuple will be the first argument, the second
352    /// element of the tuple the second argument, and so on.
353    ///
354    /// Returns an error if there is an error while executing the Lua code (eg. a function call
355    /// returns an error), if the requested return type doesn't match the actual return type, or
356    /// if we failed to push an argument.
357    ///
358    /// # Example
359    ///
360    /// ```no_run
361    /// let lua = tlua::Lua::new();
362    /// lua.exec("function sub(a, b) return a - b end").unwrap();
363    ///
364    /// let foo: tlua::LuaFunction<_> = lua.get("sub").unwrap();
365    /// let result: i32 = foo.into_call_with_args((18, 4)).unwrap();
366    /// assert_eq!(result, 14);
367    /// ```
368    ///
369    /// # Multiple return values
370    ///
371    /// ```no_run
372    /// let lua = tlua::Lua::new();
373    /// lua.exec("function divmod(a, b) return math.floor(a / b), a % b end").unwrap();
374    ///
375    /// let foo: tlua::LuaFunction<_> = lua.get("divmod").unwrap();
376    ///
377    /// let all_result: (i32, i32) = foo.into_call_with_args((18, 4)).unwrap();
378    /// assert_eq!(all_result, (4, 2));
379    /// ```
380    #[track_caller]
381    #[inline]
382    pub fn into_call_with_args<V, A>(self, args: A) -> Result<V, CallError<A::Err>>
383    where
384        A: PushInto<LuaState>,
385        V: LuaRead<PushGuard<Self>>,
386    {
387        Call::into_call_with(self, args)
388    }
389}
390
391impl<L> LuaFunction<PushGuard<L>>
392where
393    L: AsLua,
394{
395    /// Builds a new `LuaFunction` from the code of a reader.
396    ///
397    /// Returns an error if reading from the `Read` object fails or if there is a syntax error in
398    /// the code.
399    ///
400    /// # Example
401    ///
402    /// ```no_run
403    /// use std::io::Cursor;
404    ///
405    /// let lua = tlua::Lua::new();
406    ///
407    /// let f = tlua::LuaFunction::load_from_reader(&lua, Cursor::new("return 8")).unwrap();
408    /// let ret: i32 = f.call().unwrap();
409    /// assert_eq!(ret, 8);
410    /// ```
411    #[track_caller]
412    #[inline]
413    pub fn load_from_reader(lua: L, code: impl Read) -> Result<Self, LuaError> {
414        match LuaCodeFromReader::new(code).push_into_lua(lua) {
415            Ok(pushed) => unsafe { Ok(Self::new(pushed, nzi32!(-1))) },
416            Err((err, _)) => Err(err),
417        }
418    }
419
420    /// Builds a new `LuaFunction` from a raw string.
421    ///
422    /// > **Note**: This is just a wrapper around `load_from_reader`. There is no advantage in
423    /// > using `load` except that it is more convenient.
424    // TODO: remove this function? it's only a thin wrapper and it's for a very niche situation
425    #[track_caller]
426    #[inline(always)]
427    pub fn load(lua: L, code: &str) -> Result<Self, LuaError> {
428        let reader = Cursor::new(code.as_bytes());
429        Self::load_from_reader(lua, reader)
430    }
431
432    /// Loads the lua `code` and returns it as a lua function.
433    /// The provided `chunkname` will be used for the error messages.
434    #[track_caller]
435    pub fn load_file_contents(lua: L, code: &str, chunkname: &str) -> Result<Self, LuaError> {
436        let reader = Cursor::new(code.as_bytes());
437        let mut reader = LuaCodeFromReader::new(reader);
438        reader.chunkname = Some(chunkname.into());
439        match reader.push_into_lua(lua) {
440            Ok(pushed) => unsafe { Ok(Self::new(pushed, nzi32!(-1))) },
441            Err((err, _)) => Err(err),
442        }
443    }
444}