low-luajit 0.4.0

Low level but safe bindings to the LuaJIT C API with no dependencies and no-std support to create Lua modules using Rust.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
use core::{ffi::CStr, marker::PhantomData, ops::Not};
use crate::{LuaState, ffi};

macro_rules! iterator_impl {
	($iterator:ident, $assert_err:literal; $(impla {$($impl_fn:item)*};)? $($iter_fn:item)+) => {
		impl $iterator<'_> {
			/// Constructs a new iterator over the table at the given `index`.
			///
			/// # Panics
			///
			/// Panics if the element at the given `index` is not a table.
			#[inline]
			pub fn new(lua: &LuaState, index: i32) -> Self {
				let iterator = Self { lua: lua.as_ptr(), _marker: PhantomData };

				let mut lua = iterator.lua();
				lua.push_value(index);
				lua.push_nil();
				lua.push_nil();
				assert!(lua.is_table(-3), $assert_err, index);

				iterator
			}

			/// Grants special access to the Lua state for the iterator. Do not expose to the public API!
			const fn lua(&self) -> LuaState {
				// SAFETY: self.lua is always valid and the originating LuaState should be borrowed.
				// While usually this would not be safe enough (as the user can still *read* from the state),
				// this iterator does not interact with elements it has not pushed itself and said elements are all referenced in a table.
				unsafe { LuaState::from_ptr(self.lua) }
			}

			$($($impl_fn)*)?
		}

		impl Iterator for $iterator<'_> {
			type Item = (i32, i32);

			$($iter_fn)+
		}

		impl Drop for $iterator<'_> {
			#[inline]
			fn drop(&mut self) {
				self.lua().pop(3);
			}
		}
	};
}

/// Iterator over all key-value pairs of a specified table.
///
/// This iterator internally calls [`LuaState::next`] for you,
/// returning for each iteration the indices to the key and value, respectively.
/// (Currently, the returned indices are always `-2` and `-1`, this may change in the future.)
///
/// Once created, three elements will be pushed onto the stack *(the table, the key, and the value)*.
/// Each iteration overwrites the previous key-value pair's elements with the new pair's.
/// All three elements will be automatically popped once the iterator is *dropped*.
///
/// After traversing the entire table, the iterator can be reused to reiterate over the entire table again and again if needed.
///
/// Multiple `Pairs` (and [`IPairs`]) may exist simultaneously for nested iterations,
/// but the iterators should be used and dropped in the order in which they were created:
/// doing otherwise is considered a logic error and will result in unexpected behavior *(not UB)*.
///
/// Finishing the loop without properly dropping the iterator is also considered a logic error (as the elements would not be popped).
///
/// Lastly, getting the key's value using a function that can replace the number stored in the stack with a string
/// (like [`LuaState::to_string`]) may cause a logic error if said replacement occurs.
#[repr(transparent)]
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct Pairs<'a> {
	lua: *mut ffi::lua_State,
	_marker: PhantomData<&'a LuaState>,
}

iterator_impl!(
	Pairs,
	"cannot iterate over all key-value pairs of the element at index {} as it is not a table";

	#[inline]
	fn next(&mut self) -> Option<Self::Item> {
		let mut lua = self.lua();
		lua.pop(1);

		// SAFETY: The element at index -2 should always be a table.
		if unsafe { lua.next_unchecked(-2) } {
			Some((-2, -1))
		} else {
			lua.push_nil();
			lua.push_nil();
			None
		}
	}
);

/// Iterator over the indexed key-value pairs of a specified table.
///
/// This iterator returns for each iteration the indices to the key and value, respectively.
/// (Currently, the returned indices are always `-2` and `-1`, this may change in the future.)
/// Just like Lua's `ipairs` function, it starts at key `1` and increments it for each iteration;
/// stopping once the key does not contain a value in the table.
///
/// Once created, three elements will be pushed onto the stack *(the table, the value, and the key)*.
/// Each iteration overwrites the previous key-value pair's elements with the new pair's.
/// All three elements will be automatically popped once the iterator is *dropped*.
///
/// Calling `next()` after the iterator ended will still cause it to increment the key and read the next key-value pair,
/// which could be used to skip over gaps.
///
/// Multiple `IPairs` (and [`Pairs`]) may exist simultaneously for nested iterations,
/// but the iterators should be used and dropped in the order in which they were created:
/// doing otherwise is considered a logic error and will result in unexpected behavior *(not UB)*.
///
/// Finishing the loop without properly dropping the iterator is also considered a logic error (as the elements would not be popped).
#[repr(transparent)]
#[derive(Debug)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct IPairs<'a> {
	lua: *mut ffi::lua_State,
	_marker: PhantomData<&'a LuaState>,
}

iterator_impl!(
	IPairs,
	"cannot iterate over the indexed key-value pairs of the element at index {} as it is not a table";

	#[inline]
	#[allow(clippy::cast_possible_truncation)]
	fn next(&mut self) -> Option<Self::Item> {
		let mut lua = self.lua();
		let i = lua.to_integer(-2) + 1;
		lua.pop(2);

		lua.push_integer(i);
		// SAFETY: lua.0 is always valid and the element at index -2 should always be a table.
		unsafe { ffi::lua_rawgeti(lua.0, -2, i as i32); }
		lua.is_nil(-1).not().then_some((-2, -1))
	}

	#[inline]
	fn size_hint(&self) -> (usize, Option<usize>) {
		(0, Some(self.lua().object_length(-3)))
	}
);

impl IPairs<'_> {
	/// Returns the index that the iterator has last read.
	///
	/// If the iterator has not done its first iteration yet, `0` will be returned.
	#[inline]
	pub fn current_index(&self) -> usize {
		self.lua().to_integer(-2).cast_unsigned()
	}
}

/// Table related functions.
impl LuaState {
	/// Returns `true` if the value at the given acceptable `index` is a table, `false` otherwise.
	#[inline]
	#[must_use]
	pub fn is_table(&self, index: i32) -> bool {
		// SAFETY: self.0 is always valid.
		unsafe { ffi::lua_istable(self.0, index) != 0 }
	}

	/// Creates a new empty table and pushes it onto the stack.
	///
	/// To pre-allocate elements when creating a new table, see [`LuaState::create_table`].
	#[inline]
	pub fn new_table(&mut self) {
		// SAFETY: self.0 is always valid.
		unsafe { ffi::lua_newtable(self.0); }
	}

	/// Creates a new empty table and pushes it onto the stack.
	///
	/// The table will have `n_arr` array elements and `n_rec` non-array elements pre-allocated.
	///
	/// To create a new table without pre-allocating, see [`LuaState::new_table`].
	///
	/// # Lua Errors
	///
	/// Calling this function way too many times may trigger a stack overflow.
	///
	/// Incredibly high values for `n_arr` or `n_rec` may instantly trigger a table overflow.
	#[inline]
	pub fn create_table(&mut self, n_arr: i32, n_rec: i32) {
		// SAFETY: self.0 is always valid.
		unsafe { ffi::lua_createtable(self.0, n_arr, n_rec); }
	}

	/// Does the equivalent of the Lua code `t[key]`, where `t` is the value located at the given valid `index`.
	///
	/// The result is pushed onto the stack (even if `nil`).
	///
	/// For a function that does the same but allows any value instead of `key`, see [`LuaState::get_table`].
	///
	/// # Lua Errors
	///
	/// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
	///
	/// Indexing `t` may also trigger the `__index` metamethod which could error.
	#[inline]
	pub fn get_field(&mut self, index: i32, key: &CStr) {
		// SAFETY: self.0 is always valid and key's pointer is valid for the duration of this function.
		unsafe { ffi::lua_getfield(self.0, index, key.as_ptr()); }
	}

	/// Does the equivalent of the Lua code `t[key] = v`, where `t` is the value located at the given valid `index`
	/// and `v` is the value at the top of the stack (which is popped).
	///
	/// For a function that does the same but allows any value instead of `key`, see [`LuaState::set_table`].
	///
	/// # Panics
	///
	/// Panics if the stack is empty.
	///
	/// # Lua Errors
	///
	/// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
	///
	/// Indexing `t` may also trigger the `__newindex` metamethod which could error.
	#[inline]
	pub fn set_field(&mut self, index: i32, key: &CStr) {
		assert!(self.get_top() > 0, "tried to set field {key:?} for the table at index {index} despite having an empty stack");

		// SAFETY: self.0 is always valid, key's pointer is valid for the duration of this function, and the stack is not empty.
		unsafe { ffi::lua_setfield(self.0, index, key.as_ptr()); }
	}

	/// Gets the global named `name` and pushes it onto the stack (even if `nil`).
	///
	/// # Lua Errors
	///
	/// Indexing the global environment may trigger the `__index` metamethod which could error.
	#[inline]
	pub fn get_global(&mut self, name: &CStr) {
		// SAFETY: self.0 is always valid and name's pointer is valid for the duration of this function.
		unsafe { ffi::lua_getglobal(self.0, name.as_ptr()); }
	}

	/// Pops the top element and sets it as the value of the global named `name`.
	///
	/// # Panics
	///
	/// Panics if the stack is empty.
	///
	/// # Lua Errors
	///
	/// Indexing the global environment may trigger the `__newindex` metamethod which could error.
	#[inline]
	pub fn set_global(&mut self, name: &CStr) {
		assert!(self.get_top() > 0, "tried to set global {name:?} despite having an empty stack");

		// SAFETY: self.0 and name's pointer are always valid and the stack is not empty.
		unsafe { ffi::lua_setglobal(self.0, name.as_ptr()); }
	}

	/// Does the equivalent of the Lua code `t[k]`, where `t` is the value located at the given valid `index`
	/// and `k` is the value at the top of the stack (which is popped).
	///
	/// The result is pushed onto the stack, replacing `k` (even if `nil`).
	///
	/// # Panics
	///
	/// Panics if the stack is empty.
	///
	/// # Lua Errors
	///
	/// Not every value `t` can be indexed: for example, if `t` is `nil` this function will error.
	///
	/// Indexing `t` may also trigger the `__index` metamethod which could error.
	#[inline]
	pub fn get_table(&mut self, index: i32) {
		assert!(self.get_top() > 0, "tried to get a value from the table at index {index} despite having an empty stack");

		// SAFETY: self.0 is always valid and the stack is not empty.
		unsafe { ffi::lua_gettable(self.0, index); }
	}

	/// Does the equivalent of the Lua code `t[k] = v`, where `t` is the value located at the given valid `index`
	/// and `v` and `k` are the values at the top of the stack (which are popped from the stack in said order).
	///
	/// # Panics
	///
	/// Panics if the stack does not contain both `v` and `k` (thus contains less than 2 elements).
	///
	/// # Lua Errors
	///
	/// Not every value `t` or `k` can be used: if `t` or `k` are `nil` this function will error.
	///
	/// Indexing `t` may also trigger the `__newindex` metamethod which could error.
	#[inline]
	pub fn set_table(&mut self, index: i32) {
		assert!(2 <= self.get_top(), "tried to set a value for the table at index {index} despite not having `v` and `k` in the stack");

		// SAFETY: self.0 is always valid and v and k are in the stack.
		unsafe { ffi::lua_settable(self.0, index); }
	}

	/// Accesses a table similarly to [`LuaState::get_table`], but without triggering any metamethod (see [`LuaState::get_table`]).
	///
	/// # Panics
	///
	/// Panics if the element at the given valid `index` is not a table or if the stack is empty.
	#[inline]
	pub fn raw_get(&mut self, index: i32) {
		assert!(self.is_table(index), "cannot raw get a value from the element at index {index} as it is not a table");
		assert!(self.get_top() > 0, "tried to raw get a value from the table at index {index} despite having an empty stack");

		// SAFETY: self.0 is always valid, the element specified is a table and the stack is not empty.
		unsafe { ffi::lua_rawget(self.0, index); }
	}

	/// Accesses a table similarly to [`LuaState::set_table`], but without triggering any metamethod (see [`LuaState::set_table`]).
	///
	/// # Panics
	///
	/// Panics if the element at the given valid `index` is not a table or if the stack is empty.
	///
	/// # Lua errors
	///
	/// Not every value `k` can be used: if `k` is `nil` this function will error.
	#[inline]
	pub fn raw_set(&mut self, index: i32) {
		assert!(self.is_table(index), "cannot raw set a value for the element at index {index} as it is not a table");
		assert!(2 <= self.get_top(), "tried to raw set a value for the table at index {index} despite not having `v` and `k` in the stack");

		// SAFETY: self.0 is always valid, the element specified is a table and v and k are in the stack.
		unsafe { ffi::lua_rawset(self.0, index); }
	}

	/// Does the equivalent of the Lua code `t[i]`, where `t` is the value located at the given valid `index`.
	///
	/// The result is pushed onto the stack.
	///
	/// # Panics
	///
	/// Panics if the element at the given valid `index` is not a table.
	#[inline]
	pub fn raw_geti(&mut self, index: i32, i: i32) {
		assert!(self.is_table(index), "cannot raw get an element from the element at index {index} as it is not a table");

		// SAFETY: self.0 is always valid and the element specified is a table.
		unsafe { ffi::lua_rawgeti(self.0, index, i); }
	}

	/// Does the equivalent of the Lua code `t[i] = v`, where `t` is the value located at the given valid `index`
	/// and `v` is the value at the top of the stack (which is popped).
	///
	/// # Panics
	///
	/// Panics if the element at the given valid `index` is not a table or if the stack is empty.
	#[inline]
	pub fn raw_seti(&mut self, index: i32, i: i32) {
		assert!(self.is_table(index), "cannot raw set a value for the element at index {index} as it is not a table");
		assert!(self.get_top() > 0, "tried to raw set a value for the table at index {index} despite having an empty stack");

		// SAFETY: self.0 is always valid, the element specified is a table and the stack is not empty.
		unsafe { ffi::lua_rawseti(self.0, index, i); }
	}

	/// Pops the last element of the stack, treating it as the key,
	/// and pushes onto the stack the *next* key-value pair from the table at the given `index`, returning `true`.
	///
	/// If there is no next key-value pair, then nothing is pushed and `false` is returned instead.
	///
	/// This function can be used to traverse all elements of a table, see the example below.
	///
	/// # Panics
	///
	/// Panics if the element at the given `index` is not a table.
	///
	/// # Lua Errors
	///
	/// If the given key is neither `nil` nor present in the table, this function will error.
	///
	/// # Examples
	///
	/// ```
	/// # use low_luajit::{LuaState, AmountReturned};
	/// # extern "C-unwind" fn example(mut lua: LuaState) -> AmountReturned {
	/// # let t = 1;
	/// // Assume the table is located in the stack at index t.
	/// lua.push_nil(); // Push the first (dummy) key.
	/// while lua.next(t) { // Get the next key-value pair.
	///     // (The key is located at index -2, while the value is located at index -1.)
	///     // Use the key-value pair in some way...
	///     lua.pop(1); // Pop the value, so that the key can be used to get the next key-value pair.
	/// }
	/// # AmountReturned::NONE }
	/// ```
	pub fn next(&mut self, index: i32) -> bool {
		assert!(self.is_table(index), "cannot get the next key-value pair of the element at index {index} as it is not a table");

		// SAFETY: The element at the given index is a table.
		unsafe { self.next_unchecked(index) }
	}

	/// Behaves exactly as [`LuaState::next`], but without doing any checks.
	///
	/// # Safety
	///
	/// The element at the given `index` must be a table.
	pub unsafe fn next_unchecked(&mut self, index: i32) -> bool {
		// SAFETY: The caller must ensure the element at the given index is a table.
		unsafe { ffi::lua_next(self.0, index) != 0 }
	}

	/// Returns an iterator over all key-value pairs of the table at the given `index`,
	/// mimicking the behavior of Lua's `pairs` function.
	///
	/// For each iteration, the indices of the current key-value pair are returned.
	///
	/// *[See also the `Pairs` struct](struct.Pairs.html)*.
	///
	/// # Panics
	///
	/// Panics if the element at the given `index` is not a table.
	///
	/// # Examples
	///
	/// Printing the Lua standard library:
	///
	/// ```
	/// # use low_luajit::{LuaState, AmountReturned};
	/// # extern "C-unwind" fn example(lua: LuaState) -> AmountReturned {
	/// for (k, v) in lua.pairs(LuaState::GLOBALS_INDEX) {
	///     println!("{}: {}", lua.display(k), lua.display(v));
	///
	///     if lua.is_table(v) {
	///         for (k, v) in lua.pairs(v) {
	///             println!("\t{}: {}", lua.display(k), lua.display(v));
	///         }
	///     }
	/// }
	/// # AmountReturned::NONE }
	/// ```
	#[inline]
	pub fn pairs(&self, index: i32) -> Pairs<'_> {
		Pairs::new(self, index)
	}

	/// Returns an iterator over the indexed key-value pairs of the table at the given `index`,
	/// mimicking the behavior of Lua's `ipairs` function.
	///
	/// For each iteration, the indices of the current key-value pair are returned.
	///
	/// *[See also the `IPairs` struct](struct.IPairs.html)*.
	///
	/// # Panics
	///
	/// Panics if the element at the given `index` is not a table.
	#[inline]
	pub fn ipairs(&self, index: i32) -> IPairs<'_> {
		IPairs::new(self, index)
	}

	/// Gets the metatable of the value at the given acceptable `index` and pushes it onto the stack, returning `true`.
	///
	/// If `index` is invalid or the value has no metatable, nothing is pushed and the function returns `false`.
	#[inline]
	pub fn get_metatable(&mut self, index: i32) -> bool {
		// SAFETY: self.0 is always valid.
		unsafe { ffi::lua_getmetatable(self.0, index) != 0 }
	}

	/// Pops the top elemenet and sets it as the metatable of the value at the given acceptable `index`.
	///
	/// This function always returns `true`.
	///
	/// # Panics
	///
	/// Panics if the top element is not a table or if the stack is empty.
	#[inline]
	pub fn set_metatable(&mut self, index: i32) -> bool {
		assert!(self.get_top() > 0, "tried to set the metatable for the element at index {index} despite having an empty stack");
		assert!(self.is_table(-1), "cannot set the metatable for the element at index {index}, as the given metatable is not a table");

		// SAFETY: self.0 is always valid, the stack is not empty and the top element is a table.
		unsafe { ffi::lua_setmetatable(self.0, index) != 0 }
	}

	/// Gets the environment table of the value at the given `index` and pushes it onto the stack.
	///
	/// If `index` is invalid or the value has no environment table, `nil` is pushed onto the stack.
	#[inline]
	pub fn get_fenv(&mut self, index: i32) {
		// SAFETY: self.0 is always valid.
		unsafe { ffi::lua_getfenv(self.0, index); }
	}

	/// Pops a table from the stack and sets it as the environment table of the value at the given `index`, returning `true`.
	///
	/// Only functions, userdata and threads may have an environment table.
	///
	/// If `index` is invalid or the value's type is not one listed above, the stack is still popped but the function returns `false`.
	///
	/// # Panics
	///
	/// Panics if the top element is not a table or if the stack is empty.
	#[inline]
	pub fn set_fenv(&mut self, index: i32) -> bool {
		assert!(self.get_top() > 0, "tried to set the environment table for the element at index {index} despite having an empty stack");
		assert!(self.is_table(-1), "cannot set the environment table for the element at index {index}, as the given environment table is not a table");

		// SAFETY: self.0 is always valid, the stack is not empty and the top element is a table.
		unsafe { ffi::lua_setfenv(self.0, index) != 0 }
	}

	/// Calls the metamethod `method` from the metatable of the element at the given `index`.
	///
	/// The metamethod will receive the element itself as its only argument and its result will be pushed onto the stack, returning `true`.
	///
	/// If there is no metatable or no metamethod, nothing is pushed and this function returns `false` instead.
	///
	/// # Lua Errors
	///
	/// The called metamethod may error.
	#[inline]
	pub fn call_meta(&mut self, index: i32, method: &CStr) -> bool {
		// SAFETY: method's pointer is valid for the duration of this function.
		unsafe { ffi::luaL_callmeta(self.0, index, method.as_ptr()) != 0 }
	}
}