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}