Skip to main content

mlua/
conversion.rs

1use std::borrow::Cow;
2use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
3use std::ffi::{CStr, CString, OsStr, OsString};
4use std::hash::{BuildHasher, Hash};
5use std::os::raw::c_int;
6use std::path::{Path, PathBuf};
7use std::{slice, str};
8
9use bstr::{BStr, BString, ByteVec};
10use num_traits::cast;
11
12use crate::error::{Error, Result};
13use crate::function::Function;
14use crate::state::{Lua, RawLua};
15use crate::string::{BorrowedBytes, BorrowedStr, LuaString};
16use crate::table::Table;
17use crate::thread::Thread;
18use crate::traits::{FromLua, IntoLua, ShortTypeName as _};
19use crate::types::{Either, LightUserData, MaybeSend, MaybeSync, RegistryKey};
20use crate::userdata::{AnyUserData, UserData};
21use crate::value::{Nil, Value};
22
23impl IntoLua for Value {
24    #[inline]
25    fn into_lua(self, _: &Lua) -> Result<Value> {
26        Ok(self)
27    }
28}
29
30impl IntoLua for &Value {
31    #[inline]
32    fn into_lua(self, _: &Lua) -> Result<Value> {
33        Ok(self.clone())
34    }
35
36    #[inline]
37    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
38        lua.push_value(self)
39    }
40}
41
42impl FromLua for Value {
43    #[inline]
44    fn from_lua(lua_value: Value, _: &Lua) -> Result<Self> {
45        Ok(lua_value)
46    }
47}
48
49impl IntoLua for LuaString {
50    #[inline]
51    fn into_lua(self, _: &Lua) -> Result<Value> {
52        Ok(Value::String(self))
53    }
54}
55
56impl IntoLua for &LuaString {
57    #[inline]
58    fn into_lua(self, _: &Lua) -> Result<Value> {
59        Ok(Value::String(self.clone()))
60    }
61
62    #[inline]
63    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
64        lua.push_ref(&self.0);
65        Ok(())
66    }
67}
68
69impl FromLua for LuaString {
70    #[inline]
71    fn from_lua(value: Value, lua: &Lua) -> Result<LuaString> {
72        let ty = value.type_name();
73        lua.coerce_string(value)?
74            .ok_or_else(|| Error::from_lua_conversion(ty, "string", "expected string or number".to_string()))
75    }
76
77    unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
78        let state = lua.state();
79        let type_id = ffi::lua_type(state, idx);
80        if type_id == ffi::LUA_TSTRING {
81            ffi::lua_xpush(state, lua.ref_thread(), idx);
82            return Ok(LuaString(lua.try_pop_ref_thread()?));
83        }
84        // Fallback to default
85        Self::from_lua(lua.try_stack_value(idx, Some(type_id))?, lua.lua())
86    }
87}
88
89impl IntoLua for BorrowedStr {
90    #[inline]
91    fn into_lua(self, _: &Lua) -> Result<Value> {
92        Ok(Value::String(LuaString(self.vref)))
93    }
94
95    #[inline]
96    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
97        lua.push_ref(&self.vref);
98        Ok(())
99    }
100}
101
102impl IntoLua for &BorrowedStr {
103    #[inline]
104    fn into_lua(self, _: &Lua) -> Result<Value> {
105        Ok(Value::String(LuaString(self.vref.clone())))
106    }
107
108    #[inline]
109    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
110        lua.push_ref(&self.vref);
111        Ok(())
112    }
113}
114
115impl FromLua for BorrowedStr {
116    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
117        let s = LuaString::from_lua(value, lua)?;
118        BorrowedStr::try_from(&s)
119    }
120
121    unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
122        let s = LuaString::from_stack(idx, lua)?;
123        BorrowedStr::try_from(&s)
124    }
125}
126
127impl IntoLua for BorrowedBytes {
128    #[inline]
129    fn into_lua(self, _: &Lua) -> Result<Value> {
130        Ok(Value::String(LuaString(self.vref)))
131    }
132
133    #[inline]
134    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
135        lua.push_ref(&self.vref);
136        Ok(())
137    }
138}
139
140impl IntoLua for &BorrowedBytes {
141    #[inline]
142    fn into_lua(self, _: &Lua) -> Result<Value> {
143        Ok(Value::String(LuaString(self.vref.clone())))
144    }
145
146    #[inline]
147    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
148        lua.push_ref(&self.vref);
149        Ok(())
150    }
151}
152
153impl FromLua for BorrowedBytes {
154    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
155        let s = LuaString::from_lua(value, lua)?;
156        Ok(BorrowedBytes::from(&s))
157    }
158
159    unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
160        let s = LuaString::from_stack(idx, lua)?;
161        Ok(BorrowedBytes::from(&s))
162    }
163}
164
165impl IntoLua for Table {
166    #[inline]
167    fn into_lua(self, _: &Lua) -> Result<Value> {
168        Ok(Value::Table(self))
169    }
170}
171
172impl IntoLua for &Table {
173    #[inline]
174    fn into_lua(self, _: &Lua) -> Result<Value> {
175        Ok(Value::Table(self.clone()))
176    }
177
178    #[inline]
179    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
180        lua.push_ref(&self.0);
181        Ok(())
182    }
183}
184
185impl FromLua for Table {
186    #[inline]
187    fn from_lua(value: Value, _: &Lua) -> Result<Table> {
188        match value {
189            Value::Table(table) => Ok(table),
190            _ => Err(Error::from_lua_conversion(value.type_name(), "table", None)),
191        }
192    }
193}
194
195impl IntoLua for Function {
196    #[inline]
197    fn into_lua(self, _: &Lua) -> Result<Value> {
198        Ok(Value::Function(self))
199    }
200}
201
202impl IntoLua for &Function {
203    #[inline]
204    fn into_lua(self, _: &Lua) -> Result<Value> {
205        Ok(Value::Function(self.clone()))
206    }
207
208    #[inline]
209    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
210        lua.push_ref(&self.0);
211        Ok(())
212    }
213}
214
215impl FromLua for Function {
216    #[inline]
217    fn from_lua(value: Value, _: &Lua) -> Result<Function> {
218        match value {
219            Value::Function(table) => Ok(table),
220            _ => Err(Error::from_lua_conversion(value.type_name(), "function", None)),
221        }
222    }
223}
224
225impl IntoLua for Thread {
226    #[inline]
227    fn into_lua(self, _: &Lua) -> Result<Value> {
228        Ok(Value::Thread(self))
229    }
230}
231
232impl IntoLua for &Thread {
233    #[inline]
234    fn into_lua(self, _: &Lua) -> Result<Value> {
235        Ok(Value::Thread(self.clone()))
236    }
237
238    #[inline]
239    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
240        lua.push_ref(&self.0);
241        Ok(())
242    }
243}
244
245impl FromLua for Thread {
246    #[inline]
247    fn from_lua(value: Value, _: &Lua) -> Result<Thread> {
248        match value {
249            Value::Thread(t) => Ok(t),
250            _ => Err(Error::from_lua_conversion(value.type_name(), "thread", None)),
251        }
252    }
253}
254
255impl IntoLua for AnyUserData {
256    #[inline]
257    fn into_lua(self, _: &Lua) -> Result<Value> {
258        Ok(Value::UserData(self))
259    }
260}
261
262impl IntoLua for &AnyUserData {
263    #[inline]
264    fn into_lua(self, _: &Lua) -> Result<Value> {
265        Ok(Value::UserData(self.clone()))
266    }
267
268    #[inline]
269    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
270        lua.push_ref(&self.0);
271        Ok(())
272    }
273}
274
275impl FromLua for AnyUserData {
276    #[inline]
277    fn from_lua(value: Value, _: &Lua) -> Result<AnyUserData> {
278        match value {
279            Value::UserData(ud) => Ok(ud),
280            _ => Err(Error::from_lua_conversion(value.type_name(), "userdata", None)),
281        }
282    }
283}
284
285impl<T: UserData + MaybeSend + MaybeSync + 'static> IntoLua for T {
286    #[inline]
287    fn into_lua(self, lua: &Lua) -> Result<Value> {
288        Ok(Value::UserData(lua.create_userdata(self)?))
289    }
290}
291
292impl IntoLua for Error {
293    #[inline]
294    fn into_lua(self, _: &Lua) -> Result<Value> {
295        Ok(Value::Error(Box::new(self)))
296    }
297}
298
299impl FromLua for Error {
300    #[inline]
301    fn from_lua(value: Value, _: &Lua) -> Result<Error> {
302        match value {
303            Value::Error(err) => Ok(*err),
304            val => Ok(Error::runtime(val.to_string()?)),
305        }
306    }
307}
308
309#[cfg(feature = "anyhow")]
310impl IntoLua for anyhow::Error {
311    #[inline]
312    fn into_lua(self, _: &Lua) -> Result<Value> {
313        Ok(Value::Error(Box::new(Error::from(self))))
314    }
315}
316
317impl IntoLua for RegistryKey {
318    #[inline]
319    fn into_lua(self, lua: &Lua) -> Result<Value> {
320        lua.registry_value(&self)
321    }
322
323    #[inline]
324    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
325        <&RegistryKey>::push_into_stack(&self, lua)
326    }
327}
328
329impl IntoLua for &RegistryKey {
330    #[inline]
331    fn into_lua(self, lua: &Lua) -> Result<Value> {
332        lua.registry_value(self)
333    }
334
335    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
336        if !lua.owns_registry_value(self) {
337            return Err(Error::MismatchedRegistryKey);
338        }
339
340        match self.id() {
341            ffi::LUA_REFNIL => ffi::lua_pushnil(lua.state()),
342            id => {
343                ffi::lua_rawgeti(lua.state(), ffi::LUA_REGISTRYINDEX, id as _);
344            }
345        }
346        Ok(())
347    }
348}
349
350impl FromLua for RegistryKey {
351    #[inline]
352    fn from_lua(value: Value, lua: &Lua) -> Result<RegistryKey> {
353        lua.create_registry_value(value)
354    }
355}
356
357impl IntoLua for bool {
358    #[inline]
359    fn into_lua(self, _: &Lua) -> Result<Value> {
360        Ok(Value::Boolean(self))
361    }
362
363    #[inline]
364    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
365        ffi::lua_pushboolean(lua.state(), self as c_int);
366        Ok(())
367    }
368}
369
370impl FromLua for bool {
371    #[inline]
372    fn from_lua(v: Value, _: &Lua) -> Result<Self> {
373        match v {
374            Value::Nil => Ok(false),
375            Value::Boolean(b) => Ok(b),
376            _ => Ok(true),
377        }
378    }
379
380    #[inline]
381    unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
382        Ok(ffi::lua_toboolean(lua.state(), idx) != 0)
383    }
384}
385
386impl IntoLua for LightUserData {
387    #[inline]
388    fn into_lua(self, _: &Lua) -> Result<Value> {
389        Ok(Value::LightUserData(self))
390    }
391}
392
393impl FromLua for LightUserData {
394    #[inline]
395    fn from_lua(value: Value, _: &Lua) -> Result<Self> {
396        match value {
397            Value::LightUserData(ud) => Ok(ud),
398            _ => Err(Error::from_lua_conversion(
399                value.type_name(),
400                "lightuserdata",
401                None,
402            )),
403        }
404    }
405}
406
407#[cfg(feature = "luau")]
408impl IntoLua for crate::Vector {
409    #[inline]
410    fn into_lua(self, _: &Lua) -> Result<Value> {
411        Ok(Value::Vector(self))
412    }
413}
414
415#[cfg(feature = "luau")]
416impl FromLua for crate::Vector {
417    #[inline]
418    fn from_lua(value: Value, _: &Lua) -> Result<Self> {
419        match value {
420            Value::Vector(v) => Ok(v),
421            _ => Err(Error::from_lua_conversion(value.type_name(), "vector", None)),
422        }
423    }
424}
425
426#[cfg(feature = "luau")]
427impl IntoLua for crate::Buffer {
428    #[inline]
429    fn into_lua(self, _: &Lua) -> Result<Value> {
430        Ok(Value::Buffer(self))
431    }
432}
433
434#[cfg(feature = "luau")]
435impl IntoLua for &crate::Buffer {
436    #[inline]
437    fn into_lua(self, _: &Lua) -> Result<Value> {
438        Ok(Value::Buffer(self.clone()))
439    }
440
441    #[inline]
442    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
443        lua.push_ref(&self.0);
444        Ok(())
445    }
446}
447
448#[cfg(feature = "luau")]
449impl FromLua for crate::Buffer {
450    #[inline]
451    fn from_lua(value: Value, _: &Lua) -> Result<Self> {
452        match value {
453            Value::Buffer(buf) => Ok(buf),
454            _ => Err(Error::from_lua_conversion(value.type_name(), "buffer", None)),
455        }
456    }
457}
458
459impl IntoLua for String {
460    #[inline]
461    fn into_lua(self, lua: &Lua) -> Result<Value> {
462        #[cfg(feature = "lua55")]
463        if true {
464            return Ok(Value::String(lua.create_external_string(self)?));
465        }
466
467        Ok(Value::String(lua.create_string(self)?))
468    }
469
470    #[inline]
471    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
472        #[cfg(feature = "lua55")]
473        if lua.unlikely_memory_error() {
474            return crate::util::push_external_string(lua.state(), self.into(), false);
475        }
476
477        push_bytes_into_stack(self, lua)
478    }
479}
480
481impl FromLua for String {
482    #[inline]
483    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
484        let ty = value.type_name();
485        Ok(lua
486            .coerce_string(value)?
487            .ok_or_else(|| {
488                Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
489            })?
490            .to_str()?
491            .to_owned())
492    }
493
494    #[inline]
495    unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
496        let state = lua.state();
497        let type_id = ffi::lua_type(state, idx);
498        if type_id == ffi::LUA_TSTRING {
499            let mut size = 0;
500            let data = ffi::lua_tolstring(state, idx, &mut size);
501            let bytes = slice::from_raw_parts(data as *const u8, size);
502            return str::from_utf8(bytes)
503                .map(|s| s.to_owned())
504                .map_err(|e| Error::from_lua_conversion("string", Self::type_name(), e.to_string()));
505        }
506        // Fallback to default
507        Self::from_lua(lua.try_stack_value(idx, Some(type_id))?, lua.lua())
508    }
509}
510
511impl IntoLua for &str {
512    #[inline]
513    fn into_lua(self, lua: &Lua) -> Result<Value> {
514        Ok(Value::String(lua.create_string(self)?))
515    }
516
517    #[inline]
518    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
519        push_bytes_into_stack(self, lua)
520    }
521}
522
523impl IntoLua for Cow<'_, str> {
524    #[inline]
525    fn into_lua(self, lua: &Lua) -> Result<Value> {
526        match self {
527            Cow::Borrowed(s) => s.into_lua(lua),
528            Cow::Owned(s) => s.into_lua(lua),
529        }
530    }
531}
532
533impl IntoLua for Box<str> {
534    #[inline]
535    fn into_lua(self, lua: &Lua) -> Result<Value> {
536        Ok(Value::String(lua.create_string(&*self)?))
537    }
538}
539
540impl FromLua for Box<str> {
541    #[inline]
542    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
543        let ty = value.type_name();
544        Ok(lua
545            .coerce_string(value)?
546            .ok_or_else(|| {
547                Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
548            })?
549            .to_str()?
550            .to_owned()
551            .into_boxed_str())
552    }
553}
554
555impl IntoLua for CString {
556    #[inline]
557    fn into_lua(self, lua: &Lua) -> Result<Value> {
558        #[cfg(feature = "lua55")]
559        if true {
560            return Ok(Value::String(lua.create_external_string(self)?));
561        }
562
563        Ok(Value::String(lua.create_string(self.as_bytes())?))
564    }
565}
566
567impl FromLua for CString {
568    #[inline]
569    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
570        let ty = value.type_name();
571        let string = lua.coerce_string(value)?.ok_or_else(|| {
572            Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
573        })?;
574        match CStr::from_bytes_with_nul(&string.as_bytes_with_nul()) {
575            Ok(s) => Ok(s.into()),
576            Err(err) => Err(Error::from_lua_conversion(ty, Self::type_name(), err.to_string())),
577        }
578    }
579}
580
581impl IntoLua for &CStr {
582    #[inline]
583    fn into_lua(self, lua: &Lua) -> Result<Value> {
584        Ok(Value::String(lua.create_string(self.to_bytes())?))
585    }
586}
587
588impl IntoLua for Cow<'_, CStr> {
589    #[inline]
590    fn into_lua(self, lua: &Lua) -> Result<Value> {
591        match self {
592            Cow::Borrowed(s) => s.into_lua(lua),
593            Cow::Owned(s) => s.into_lua(lua),
594        }
595    }
596}
597
598impl IntoLua for BString {
599    #[inline]
600    fn into_lua(self, lua: &Lua) -> Result<Value> {
601        #[cfg(feature = "lua55")]
602        if true {
603            return Ok(Value::String(lua.create_external_string(self)?));
604        }
605
606        Ok(Value::String(lua.create_string(self)?))
607    }
608}
609
610impl FromLua for BString {
611    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
612        let ty = value.type_name();
613        match value {
614            Value::String(s) => Ok((*s.as_bytes()).into()),
615            #[cfg(feature = "luau")]
616            Value::Buffer(buf) => Ok(buf.to_vec().into()),
617            _ => Ok((*lua
618                .coerce_string(value)?
619                .ok_or_else(|| {
620                    Error::from_lua_conversion(ty, Self::type_name(), "expected string or number".to_string())
621                })?
622                .as_bytes())
623            .into()),
624        }
625    }
626
627    unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
628        let state = lua.state();
629        match ffi::lua_type(state, idx) {
630            ffi::LUA_TSTRING => {
631                let mut size = 0;
632                let data = ffi::lua_tolstring(state, idx, &mut size);
633                Ok(slice::from_raw_parts(data as *const u8, size).into())
634            }
635            #[cfg(feature = "luau")]
636            ffi::LUA_TBUFFER => {
637                let mut size = 0;
638                let buf = ffi::lua_tobuffer(state, idx, &mut size);
639                mlua_assert!(!buf.is_null(), "invalid Luau buffer");
640                Ok(slice::from_raw_parts(buf as *const u8, size).into())
641            }
642            type_id => {
643                // Fallback to default
644                Self::from_lua(lua.try_stack_value(idx, Some(type_id))?, lua.lua())
645            }
646        }
647    }
648}
649
650impl IntoLua for &BStr {
651    #[inline]
652    fn into_lua(self, lua: &Lua) -> Result<Value> {
653        Ok(Value::String(lua.create_string(self)?))
654    }
655}
656
657impl IntoLua for OsString {
658    #[inline]
659    fn into_lua(self, lua: &Lua) -> Result<Value> {
660        self.as_os_str().into_lua(lua)
661    }
662}
663
664impl FromLua for OsString {
665    #[inline]
666    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
667        let ty = value.type_name();
668        let bs = BString::from_lua(value, lua)?;
669        Vec::from(bs)
670            .into_os_string()
671            .map_err(|err| Error::from_lua_conversion(ty, "OsString", err.to_string()))
672    }
673}
674
675impl IntoLua for &OsStr {
676    #[cfg(unix)]
677    #[inline]
678    fn into_lua(self, lua: &Lua) -> Result<Value> {
679        use std::os::unix::ffi::OsStrExt;
680        Ok(Value::String(lua.create_string(self.as_bytes())?))
681    }
682
683    // On non-Unix platforms `OsStr` is not guaranteed to be valid Unicode, invalid sequences are
684    // replaced with `U+FFFD` rather than erroring.
685    #[cfg(not(unix))]
686    #[inline]
687    fn into_lua(self, lua: &Lua) -> Result<Value> {
688        self.display().to_string().into_lua(lua)
689    }
690}
691
692impl IntoLua for PathBuf {
693    #[inline]
694    fn into_lua(self, lua: &Lua) -> Result<Value> {
695        self.as_os_str().into_lua(lua)
696    }
697}
698
699impl FromLua for PathBuf {
700    #[inline]
701    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
702        OsString::from_lua(value, lua).map(PathBuf::from)
703    }
704}
705
706impl IntoLua for &Path {
707    #[inline]
708    fn into_lua(self, lua: &Lua) -> Result<Value> {
709        self.as_os_str().into_lua(lua)
710    }
711}
712
713impl IntoLua for char {
714    #[inline]
715    fn into_lua(self, lua: &Lua) -> Result<Value> {
716        let mut char_bytes = [0; 4];
717        self.encode_utf8(&mut char_bytes);
718        Ok(Value::String(lua.create_string(&char_bytes[..self.len_utf8()])?))
719    }
720}
721
722impl FromLua for char {
723    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
724        let ty = value.type_name();
725        match value {
726            Value::Integer(i) => cast(i).and_then(char::from_u32).ok_or_else(|| {
727                let msg = "integer out of range when converting to char";
728                Error::from_lua_conversion(ty, "char", msg.to_string())
729            }),
730            Value::String(s) => {
731                let str = s.to_str()?;
732                let mut str_iter = str.chars();
733                match (str_iter.next(), str_iter.next()) {
734                    (Some(char), None) => Ok(char),
735                    _ => {
736                        let msg = "expected string to have exactly one char when converting to char";
737                        Err(Error::from_lua_conversion(ty, "char", msg.to_string()))
738                    }
739                }
740            }
741            _ => {
742                let msg = "expected string or integer";
743                Err(Error::from_lua_conversion(ty, Self::type_name(), msg.to_string()))
744            }
745        }
746    }
747}
748
749#[inline]
750unsafe fn push_bytes_into_stack<T>(this: T, lua: &RawLua) -> Result<()>
751where
752    T: IntoLua + AsRef<[u8]>,
753{
754    let bytes = this.as_ref();
755    if lua.unlikely_memory_error() && bytes.len() < (1 << 30) {
756        // Fast path: push directly into the Lua stack.
757        ffi::lua_pushlstring(lua.state(), bytes.as_ptr() as *const _, bytes.len());
758        return Ok(());
759    }
760    // Fallback to default
761    lua.push_value(&T::into_lua(this, lua.lua())?)
762}
763
764macro_rules! lua_convert_int {
765    ($x:ty) => {
766        impl IntoLua for $x {
767            #[inline]
768            fn into_lua(self, _: &Lua) -> Result<Value> {
769                Ok(cast(self)
770                    .map(Value::Integer)
771                    .unwrap_or_else(|| Value::Number(self as ffi::lua_Number)))
772            }
773
774            #[inline]
775            unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
776                match cast(self) {
777                    Some(i) => ffi::lua_pushinteger(lua.state(), i),
778                    None => ffi::lua_pushnumber(lua.state(), self as ffi::lua_Number),
779                }
780                Ok(())
781            }
782        }
783
784        impl FromLua for $x {
785            #[inline]
786            fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
787                let ty = value.type_name();
788                let cast_number = |n: ffi::lua_Number| {
789                    cast(n).filter(|_| {
790                        cfg!(any(
791                            feature = "lua51",
792                            feature = "lua52",
793                            feature = "luajit"
794                        )) || n == n.trunc()
795                    })
796                };
797                (match value {
798                    Value::Integer(i) => cast(i),
799                    Value::Number(n) => cast_number(n),
800                    _ => {
801                        if let Some(i) = lua.coerce_integer(value.clone())? {
802                            cast(i)
803                        } else {
804                            cast_number(lua.coerce_number(value)?.ok_or_else(|| {
805                                let msg = "expected number or string coercible to number";
806                                Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
807                            })?)
808                        }
809                    }
810                })
811                .ok_or_else(|| Error::from_lua_conversion(ty, stringify!($x), "out of range".to_string()))
812            }
813
814            unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
815                let state = lua.state();
816                let type_id = ffi::lua_type(state, idx);
817                if type_id == ffi::LUA_TNUMBER {
818                    let mut ok = 0;
819                    let i = ffi::lua_tointegerx(state, idx, &mut ok);
820                    if ok != 0 {
821                        return cast(i).ok_or_else(|| {
822                            Error::from_lua_conversion("integer", stringify!($x), "out of range".to_string())
823                        });
824                    }
825                }
826                #[cfg(feature = "luau")]
827                if type_id == ffi::LUA_TINTEGER {
828                    let i = ffi::lua_tointeger64(state, idx, std::ptr::null_mut());
829                    return cast(i).ok_or_else(|| {
830                        Error::from_lua_conversion("integer", stringify!($x), "out of range".to_string())
831                    });
832                }
833                // Fallback to default
834                Self::from_lua(lua.try_stack_value(idx, Some(type_id))?, lua.lua())
835            }
836        }
837    };
838}
839
840lua_convert_int!(i8);
841lua_convert_int!(u8);
842lua_convert_int!(i16);
843lua_convert_int!(u16);
844lua_convert_int!(i32);
845lua_convert_int!(u32);
846lua_convert_int!(i64);
847lua_convert_int!(u64);
848lua_convert_int!(i128);
849lua_convert_int!(u128);
850lua_convert_int!(isize);
851lua_convert_int!(usize);
852
853macro_rules! lua_convert_float {
854    ($x:ty) => {
855        impl IntoLua for $x {
856            #[inline]
857            fn into_lua(self, _: &Lua) -> Result<Value> {
858                Ok(Value::Number(self as _))
859            }
860        }
861
862        impl FromLua for $x {
863            #[inline]
864            fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
865                let ty = value.type_name();
866                lua.coerce_number(value)?.map(|n| n as $x).ok_or_else(|| {
867                    let msg = "expected number or string coercible to number";
868                    Error::from_lua_conversion(ty, stringify!($x), msg.to_string())
869                })
870            }
871
872            unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
873                let state = lua.state();
874                let type_id = ffi::lua_type(state, idx);
875                if type_id == ffi::LUA_TNUMBER {
876                    return Ok(ffi::lua_tonumber(state, idx) as _);
877                }
878                // Fallback to default
879                Self::from_lua(lua.try_stack_value(idx, Some(type_id))?, lua.lua())
880            }
881        }
882    };
883}
884
885lua_convert_float!(f32);
886lua_convert_float!(f64);
887
888impl<T> IntoLua for &[T]
889where
890    T: IntoLua + Clone,
891{
892    #[inline]
893    fn into_lua(self, lua: &Lua) -> Result<Value> {
894        Ok(Value::Table(lua.create_sequence_from(self.iter().cloned())?))
895    }
896}
897
898impl<T, const N: usize> IntoLua for [T; N]
899where
900    T: IntoLua,
901{
902    #[inline]
903    fn into_lua(self, lua: &Lua) -> Result<Value> {
904        Ok(Value::Table(lua.create_sequence_from(self)?))
905    }
906}
907
908impl<T, const N: usize> FromLua for [T; N]
909where
910    T: FromLua,
911{
912    #[inline]
913    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
914        match value {
915            #[cfg(feature = "luau")]
916            Value::Vector(v) if N == crate::Vector::SIZE => {
917                use std::mem::MaybeUninit;
918                let x = T::from_lua(Value::Number(v.x() as _), _lua)?;
919                let y = T::from_lua(Value::Number(v.y() as _), _lua)?;
920                let z = T::from_lua(Value::Number(v.z() as _), _lua)?;
921                #[cfg(feature = "luau-vector4")]
922                let w = T::from_lua(Value::Number(v.w() as _), _lua)?;
923                let mut arr: [MaybeUninit<T>; N] = [const { MaybeUninit::uninit() }; N];
924                arr[0].write(x);
925                arr[1].write(y);
926                arr[2].write(z);
927                #[cfg(feature = "luau-vector4")]
928                arr[3].write(w);
929                Ok(arr.map(|e| unsafe { e.assume_init() }))
930            }
931            Value::Table(table) => {
932                let vec = table.sequence_values().collect::<Result<Vec<_>>>()?;
933                vec.try_into().map_err(|vec: Vec<T>| {
934                    let msg = format!("expected table of length {N}, got {}", vec.len());
935                    Error::from_lua_conversion("table", Self::type_name(), msg)
936                })
937            }
938            _ => {
939                let msg = format!("expected table of length {N}");
940                let err = Error::from_lua_conversion(value.type_name(), Self::type_name(), msg.to_string());
941                Err(err)
942            }
943        }
944    }
945}
946
947impl<T: IntoLua> IntoLua for Box<[T]> {
948    #[inline]
949    fn into_lua(self, lua: &Lua) -> Result<Value> {
950        Ok(Value::Table(lua.create_sequence_from(self.into_vec())?))
951    }
952}
953
954impl<T: FromLua> FromLua for Box<[T]> {
955    #[inline]
956    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
957        Ok(Vec::<T>::from_lua(value, lua)?.into_boxed_slice())
958    }
959}
960
961impl<T: IntoLua> IntoLua for Vec<T> {
962    #[inline]
963    fn into_lua(self, lua: &Lua) -> Result<Value> {
964        Ok(Value::Table(lua.create_sequence_from(self)?))
965    }
966}
967
968impl<T: FromLua> FromLua for Vec<T> {
969    #[inline]
970    fn from_lua(value: Value, _lua: &Lua) -> Result<Self> {
971        match value {
972            Value::Table(table) => table.sequence_values().collect(),
973            _ => Err(Error::from_lua_conversion(
974                value.type_name(),
975                Self::type_name(),
976                "expected table".to_string(),
977            )),
978        }
979    }
980}
981
982impl<K: Eq + Hash + IntoLua, V: IntoLua, S: BuildHasher> IntoLua for HashMap<K, V, S> {
983    #[inline]
984    fn into_lua(self, lua: &Lua) -> Result<Value> {
985        Ok(Value::Table(lua.create_table_from(self)?))
986    }
987}
988
989impl<K: Eq + Hash + FromLua, V: FromLua, S: BuildHasher + Default> FromLua for HashMap<K, V, S> {
990    #[inline]
991    fn from_lua(value: Value, _: &Lua) -> Result<Self> {
992        match value {
993            Value::Table(table) => table.pairs().collect(),
994            _ => Err(Error::from_lua_conversion(
995                value.type_name(),
996                Self::type_name(),
997                "expected table".to_string(),
998            )),
999        }
1000    }
1001}
1002
1003impl<K: Ord + IntoLua, V: IntoLua> IntoLua for BTreeMap<K, V> {
1004    #[inline]
1005    fn into_lua(self, lua: &Lua) -> Result<Value> {
1006        Ok(Value::Table(lua.create_table_from(self)?))
1007    }
1008}
1009
1010impl<K: Ord + FromLua, V: FromLua> FromLua for BTreeMap<K, V> {
1011    #[inline]
1012    fn from_lua(value: Value, _: &Lua) -> Result<Self> {
1013        match value {
1014            Value::Table(table) => table.pairs().collect(),
1015            _ => Err(Error::from_lua_conversion(
1016                value.type_name(),
1017                Self::type_name(),
1018                "expected table".to_string(),
1019            )),
1020        }
1021    }
1022}
1023
1024impl<T: Eq + Hash + IntoLua, S: BuildHasher> IntoLua for HashSet<T, S> {
1025    #[inline]
1026    fn into_lua(self, lua: &Lua) -> Result<Value> {
1027        Ok(Value::Table(
1028            lua.create_table_from(self.into_iter().map(|val| (val, true)))?,
1029        ))
1030    }
1031}
1032
1033impl<T: Eq + Hash + FromLua, S: BuildHasher + Default> FromLua for HashSet<T, S> {
1034    #[inline]
1035    fn from_lua(value: Value, _: &Lua) -> Result<Self> {
1036        match value {
1037            Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
1038            Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
1039            _ => Err(Error::from_lua_conversion(
1040                value.type_name(),
1041                Self::type_name(),
1042                "expected table".to_string(),
1043            )),
1044        }
1045    }
1046}
1047
1048impl<T: Ord + IntoLua> IntoLua for BTreeSet<T> {
1049    #[inline]
1050    fn into_lua(self, lua: &Lua) -> Result<Value> {
1051        Ok(Value::Table(
1052            lua.create_table_from(self.into_iter().map(|val| (val, true)))?,
1053        ))
1054    }
1055}
1056
1057impl<T: Ord + FromLua> FromLua for BTreeSet<T> {
1058    #[inline]
1059    fn from_lua(value: Value, _: &Lua) -> Result<Self> {
1060        match value {
1061            Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
1062            Value::Table(table) => table.pairs::<T, Value>().map(|res| res.map(|(k, _)| k)).collect(),
1063            _ => Err(Error::from_lua_conversion(
1064                value.type_name(),
1065                Self::type_name(),
1066                "expected table".to_string(),
1067            )),
1068        }
1069    }
1070}
1071
1072impl<T: IntoLua> IntoLua for Option<T> {
1073    #[inline]
1074    fn into_lua(self, lua: &Lua) -> Result<Value> {
1075        match self {
1076            Some(val) => val.into_lua(lua),
1077            None => Ok(Nil),
1078        }
1079    }
1080
1081    #[inline]
1082    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
1083        match self {
1084            Some(val) => val.push_into_stack(lua)?,
1085            None => ffi::lua_pushnil(lua.state()),
1086        }
1087        Ok(())
1088    }
1089}
1090
1091impl<T: FromLua> FromLua for Option<T> {
1092    #[inline]
1093    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1094        match value {
1095            Nil => Ok(None),
1096            value => Ok(Some(T::from_lua(value, lua)?)),
1097        }
1098    }
1099
1100    #[inline]
1101    unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
1102        match ffi::lua_type(lua.state(), idx) {
1103            ffi::LUA_TNIL => Ok(None),
1104            _ => Ok(Some(T::from_stack(idx, lua)?)),
1105        }
1106    }
1107}
1108
1109impl<L: IntoLua, R: IntoLua> IntoLua for Either<L, R> {
1110    #[inline]
1111    fn into_lua(self, lua: &Lua) -> Result<Value> {
1112        match self {
1113            Either::Left(l) => l.into_lua(lua),
1114            Either::Right(r) => r.into_lua(lua),
1115        }
1116    }
1117
1118    #[inline]
1119    unsafe fn push_into_stack(self, lua: &RawLua) -> Result<()> {
1120        match self {
1121            Either::Left(l) => l.push_into_stack(lua),
1122            Either::Right(r) => r.push_into_stack(lua),
1123        }
1124    }
1125}
1126
1127impl<L: FromLua, R: FromLua> FromLua for Either<L, R> {
1128    #[inline]
1129    fn from_lua(value: Value, lua: &Lua) -> Result<Self> {
1130        let value_type_name = value.type_name();
1131        L::from_lua(value.clone(), lua)
1132            .map(Either::Left)
1133            .or_else(|_| R::from_lua(value, lua).map(Either::Right))
1134            .map_err(|_| Error::from_lua_conversion(value_type_name, Self::type_name(), None))
1135    }
1136
1137    #[inline]
1138    unsafe fn from_stack(idx: c_int, lua: &RawLua) -> Result<Self> {
1139        L::from_stack(idx, lua)
1140            .map(Either::Left)
1141            .or_else(|_| R::from_stack(idx, lua).map(Either::Right))
1142            .map_err(|_| {
1143                let state = lua.state();
1144                let from_type_name = CStr::from_ptr(ffi::lua_typename(state, ffi::lua_type(state, idx)))
1145                    .to_str()
1146                    .unwrap_or("unknown");
1147                Error::from_lua_conversion(from_type_name, Self::type_name(), None)
1148            })
1149    }
1150}