string_enum/
string_enum.rs

1//! Representing enum values with strings in Lua.
2
3use core::ffi::c_int;
4use lunka::prelude::*;
5
6pub unsafe trait LuaEnum<const N: usize> {
7	const OPTIONS: &'static lunka::AuxOptions<'static, N>;
8	unsafe fn from_index_unchecked(index: usize) -> Self;
9}
10
11#[derive(Debug)]
12enum SocketKind {
13	Udp,
14	Tcp,
15	UnixUdp,
16	UnixTcp,
17}
18
19unsafe impl LuaEnum<4> for SocketKind {
20	const OPTIONS: &'static LuaAuxOptions<'static, 4> = &LuaAuxOptions::new([
21		c"udp",
22		c"tcp",
23		c"unix udp",
24		c"unix tcp",
25	]);
26	unsafe fn from_index_unchecked(index: usize) -> Self {
27		match index {
28			0 => Self::Udp,
29			1 => Self::Tcp,
30			2 => Self::UnixUdp,
31			3 => Self::UnixTcp,
32			_ => ::core::hint::unreachable_unchecked(),
33		}
34	}
35}
36
37trait ThreadExt {
38	fn check_enum<E: LuaEnum<N>, const N: usize>(&self, arg: c_int) -> E;
39}
40
41impl ThreadExt for LuaThread {
42	fn check_enum<E: LuaEnum<N>, const N: usize>(&self, arg: c_int) -> E {
43		let index = self.check_option(arg, None, E::OPTIONS);
44		unsafe { E::from_index_unchecked(index) }
45	}
46}
47
48unsafe extern "C-unwind" fn l_test(l: *mut LuaState) -> c_int {
49	let lua = unsafe { LuaThread::from_ptr(l) };
50	let socket_kind: SocketKind = lua.check_enum(1);
51	println!("socket kind: {socket_kind:?}");
52	0
53}
54
55unsafe extern "C-unwind" fn l_main(l: *mut LuaState) -> c_int {
56	let lua = unsafe { LuaThread::from_ptr_mut(l) };
57	let mut test = move |variant: &[u8]| {
58		lua.run_managed(move |mut mg| {
59			mg.push_c_function(l_test);
60			mg.push_string(variant);
61			unsafe { mg.call(1, 0) }
62		})
63	};
64	test(b"udp");
65	test(b"tcp");
66	test(b"unix udp");
67	test(b"unix tcp");
68	test(b"invalid");
69	0
70}
71
72fn main() {
73	let mut lua = Lua::new();
74	let did_run_ok = lua.run_managed(move |mut mg| {
75		mg.push_c_function(l_main);
76		unsafe { mg.pcall(0, 1, 0).is_ok() }
77	});
78	assert!(!did_run_ok, "test code should fail");
79}