use std::borrow::Cow;
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
use std::ffi::{CStr, CString, OsStr, OsString};
use std::hash::{BuildHasher, Hash};
use std::path::{Path, PathBuf};
use bstr::{BString, ByteVec};
use luau_vm::thread::StackGuard;
use crate::error::Error;
use crate::lua::{Lua, LuaRef};
use crate::string::LuaString;
use crate::table::Table;
use crate::thread::Thread;
use crate::value::{LuaType, MultiValue, Value};
pub trait IntoLua<'lua>: Sized {
fn into_lua(self, lua: LuaRef<'lua>) -> Result<Value<'lua>, Error>;
#[doc(hidden)]
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
let value = self.into_lua(thread.lua_ref())?;
value.push_to(thread)
}
}
pub trait FromLua<'lua>: Sized {
fn from_lua(value: Value<'lua>, lua: LuaRef<'lua>) -> Result<Self, Error>;
#[doc(hidden)]
unsafe fn from_stack(thread: &Thread<'lua>, index: i32) -> Result<Self, Error> {
let value = Value::from_stack(thread, index)?;
Self::from_lua(value, thread.lua_ref())
}
}
pub trait IntoLuaMulti<'lua>: Sized {
fn into_lua_multi(self, lua: LuaRef<'lua>) -> Result<MultiValue<'lua>, Error>;
#[doc(hidden)]
unsafe fn push_into_stack_multi(self, thread: &Thread<'lua>) -> Result<usize, Error> {
let values = self.into_lua_multi(thread.lua_ref())?;
let count = values.len();
thread.reserve_stack(count)?;
for value in values {
value.push_to(thread)?;
}
Ok(count)
}
}
pub trait FromLuaMulti<'lua>: Sized {
fn from_lua_multi(values: MultiValue<'lua>, lua: LuaRef<'lua>) -> Result<Self, Error>;
#[doc(hidden)]
unsafe fn from_stack_multi(
thread: &Thread<'lua>,
base_top: i32,
count: i32,
) -> Result<Self, Error> {
let values = MultiValue::from_stack(thread, base_top, count)?;
Self::from_lua_multi(values, thread.lua_ref())
}
}
impl Lua {
pub fn pack<'lua>(&'lua self, value: impl IntoLua<'lua>) -> Result<Value<'lua>, Error> {
self.lua_ref().pack(value)
}
pub fn unpack<'lua, T>(&'lua self, value: Value<'lua>) -> Result<T, Error>
where
T: FromLua<'lua>,
{
self.lua_ref().unpack(value)
}
pub fn pack_multi<'lua>(
&'lua self,
values: impl IntoLuaMulti<'lua>,
) -> Result<MultiValue<'lua>, Error> {
self.lua_ref().pack_multi(values)
}
pub fn unpack_multi<'lua, T>(&'lua self, values: MultiValue<'lua>) -> Result<T, Error>
where
T: FromLuaMulti<'lua>,
{
self.lua_ref().unpack_multi(values)
}
pub fn convert<'lua, T>(&'lua self, value: impl IntoLua<'lua>) -> Result<T, Error>
where
T: FromLua<'lua>,
{
self.lua_ref().convert(value)
}
pub fn coerce_string<'lua>(
&'lua self,
value: Value<'lua>,
) -> Result<Option<LuaString<'lua>>, Error> {
self.lua_ref().coerce_string(value)
}
pub fn coerce_integer<'lua>(&'lua self, value: Value<'lua>) -> Result<Option<i64>, Error> {
self.lua_ref().coerce_integer(value)
}
pub fn coerce_number<'lua>(&'lua self, value: Value<'lua>) -> Result<Option<f64>, Error> {
self.lua_ref().coerce_number(value)
}
#[allow(private_bounds)]
pub fn type_metatable<T: LuaType>(&self) -> Result<Option<Table<'_>>, Error> {
self.lua_ref().type_metatable::<T>()
}
#[allow(private_bounds)]
pub fn set_type_metatable<T: LuaType>(
&self,
metatable: Option<Table<'_>>,
) -> Result<(), Error> {
self.lua_ref().set_type_metatable::<T>(metatable)
}
}
impl<'lua> LuaRef<'lua> {
pub fn pack(&self, value: impl IntoLua<'lua>) -> Result<Value<'lua>, Error> {
value.into_lua(*self)
}
pub fn unpack<T>(&self, value: Value<'lua>) -> Result<T, Error>
where
T: FromLua<'lua>,
{
T::from_lua(value, *self)
}
pub fn pack_multi(&self, values: impl IntoLuaMulti<'lua>) -> Result<MultiValue<'lua>, Error> {
values.into_lua_multi(*self)
}
pub fn unpack_multi<T>(&self, values: MultiValue<'lua>) -> Result<T, Error>
where
T: FromLuaMulti<'lua>,
{
T::from_lua_multi(values, *self)
}
pub fn convert<T>(&self, value: impl IntoLua<'lua>) -> Result<T, Error>
where
T: FromLua<'lua>,
{
T::from_lua(value.into_lua(*self)?, *self)
}
pub fn coerce_string(&self, value: Value<'lua>) -> Result<Option<LuaString<'lua>>, Error> {
unsafe {
let thread = self.current_thread();
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
value.push_to(&thread)?;
if vm_thread
.to_string(-1)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))?
.is_none()
{
return Ok(None);
}
LuaString::from_stack(&thread, -1)
.map(Some)
.map_err(|exit| Error::from_thread_exit(vm_thread, exit))
}
}
pub fn coerce_integer(&self, value: Value<'lua>) -> Result<Option<i64>, Error> {
unsafe {
let thread = self.current_thread();
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
value.push_to(&thread)?;
Ok(vm_thread.to_integer64(-1))
}
}
pub fn coerce_number(&self, value: Value<'lua>) -> Result<Option<f64>, Error> {
unsafe {
let thread = self.current_thread();
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
value.push_to(&thread)?;
Ok(vm_thread.to_number(-1))
}
}
#[allow(private_bounds)]
pub fn type_metatable<T: LuaType>(&self) -> Result<Option<Table<'lua>>, Error> {
unsafe {
let thread = self.as_vm();
let _stack = StackGuard::new(thread);
let safe_thread = self.current_thread();
T::push_type_key(thread)?;
if thread
.get_metatable(-1)
.map_err(|exit| Error::from_thread_exit(thread, exit))?
== 0
{
return Ok(None);
}
Table::from_stack(&safe_thread, -1)
.map(Some)
.map_err(|exit| Error::from_thread_exit(thread, exit))
}
}
#[allow(private_bounds)]
pub fn set_type_metatable<T: LuaType>(
&self,
metatable: Option<Table<'_>>,
) -> Result<(), Error> {
self.runtime().invalidate_managed_safe_env();
unsafe {
let thread = self.as_vm();
let _stack = StackGuard::new(thread);
let safe_thread = self.current_thread();
T::push_type_key(thread)?;
match metatable {
Some(metatable) => metatable.push_to(&safe_thread)?,
None => thread
.push_nil()
.map_err(|exit| Error::from_thread_exit(thread, exit))?,
}
thread
.set_metatable(-2)
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
}
Ok(())
}
}
impl<'lua> IntoLua<'lua> for bool {
fn into_lua(self, _: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
Ok(Value::Boolean(self))
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe {
thread
.as_vm()
.push_boolean(i32::from(self))
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
}
Ok(())
}
}
impl<'lua> FromLua<'lua> for bool {
fn from_lua(value: Value<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
Ok(match value {
Value::Nil => false,
Value::Boolean(value) => value,
_ => true,
})
}
unsafe fn from_stack(thread: &Thread<'lua>, index: i32) -> Result<Self, Error> {
Ok(unsafe { thread.as_vm().to_boolean(index) != 0 })
}
}
fn coerce_string_to_number<'lua>(
value: &LuaString<'lua>,
lua: LuaRef<'lua>,
) -> Result<Option<f64>, Error> {
unsafe {
let thread = lua.current_thread();
let vm_thread = thread.as_vm();
let _stack = StackGuard::new(vm_thread);
value.push_to(vm_thread)?;
Ok(vm_thread.to_number(-1))
}
}
macro_rules! impl_from_lua_integer {
($ty:ty) => {
impl<'lua> FromLua<'lua> for $ty {
fn from_lua(value: Value<'lua>, lua: LuaRef<'lua>) -> Result<Self, Error> {
let from = value.type_name();
let value = match value {
Value::Integer(value) => num_traits::cast(value),
Value::Number(value) => num_traits::cast(value),
Value::String(value) => match coerce_string_to_number(&value, lua)? {
Some(value) => num_traits::cast(value),
None => {
return Err(Error::from_lua_conversion(
from,
stringify!($ty),
Some("expected number or string coercible to number"),
));
}
},
_ => {
return Err(Error::from_lua_conversion(
from,
stringify!($ty),
Some("expected number or string coercible to number"),
));
}
};
value.ok_or_else(|| {
Error::from_lua_conversion(from, stringify!($ty), Some("out of range"))
})
}
unsafe fn from_stack(thread: &Thread<'lua>, index: i32) -> Result<Self, Error> {
let from = thread.stack_type_name(index);
unsafe {
let vm_thread = thread.as_vm();
if let Some(value) = vm_thread.to_integer64(index) {
return num_traits::cast(value).ok_or_else(|| {
Error::from_lua_conversion(
from.as_str(),
stringify!($ty),
Some("out of range"),
)
});
}
if let Some(value) = vm_thread.to_number(index) {
return num_traits::cast(value).ok_or_else(|| {
Error::from_lua_conversion(
from.as_str(),
stringify!($ty),
Some("out of range"),
)
});
}
}
Err(Error::from_lua_conversion(
from.as_str(),
stringify!($ty),
Some("expected number or string coercible to number"),
))
}
}
};
}
macro_rules! impl_lua_number_integer {
($($ty:ty),* $(,)?) => {
$(
impl<'lua> IntoLua<'lua> for $ty {
fn into_lua(self, _: LuaRef<'lua>) -> Result<Value<'lua>, Error> {
Ok(Value::Number(self as f64))
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe {
thread
.as_vm()
.push_number(self as f64)
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
}
Ok(())
}
}
impl_from_lua_integer!($ty);
)*
};
}
macro_rules! impl_lua_wide_integer {
($($ty:ty),* $(,)?) => {
$(
impl<'lua> IntoLua<'lua> for $ty {
fn into_lua(self, _: LuaRef<'lua>) -> Result<Value<'lua>, Error> {
if let Ok(value) = i64::try_from(self) {
return Ok(Value::Integer(value));
}
let number = self as f64;
if num_traits::cast::<_, $ty>(number) == Some(self) {
Ok(Value::Number(number))
} else {
Err(Error::into_lua_conversion(
stringify!($ty),
"number",
Some("out of range"),
))
}
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe {
if let Ok(value) = i64::try_from(self) {
thread
.as_vm()
.push_integer64(value)
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
return Ok(());
}
let number = self as f64;
if num_traits::cast::<_, $ty>(number) == Some(self) {
thread
.as_vm()
.push_number(number)
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
Ok(())
} else {
Err(Error::into_lua_conversion(
stringify!($ty),
"number",
Some("out of range"),
))
}
}
}
}
impl_from_lua_integer!($ty);
)*
};
}
impl<'lua> IntoLua<'lua> for i64 {
fn into_lua(self, _: LuaRef<'lua>) -> Result<Value<'lua>, Error> {
Ok(Value::Integer(self))
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe {
thread
.as_vm()
.push_integer64(self)
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
}
Ok(())
}
}
impl_from_lua_integer!(i64);
macro_rules! impl_lua_pointer_integer {
($($ty:ty),* $(,)?) => {
$(
impl<'lua> IntoLua<'lua> for $ty {
fn into_lua(self, _: LuaRef<'lua>) -> Result<Value<'lua>, Error> {
let number = self as f64;
if num_traits::cast::<_, $ty>(number) == Some(self) {
Ok(Value::Number(number))
} else {
Err(Error::into_lua_conversion(
stringify!($ty),
"number",
Some("out of range"),
))
}
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe {
let number = self as f64;
if num_traits::cast::<_, $ty>(number) == Some(self) {
thread
.as_vm()
.push_number(number)
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
} else {
return Err(Error::into_lua_conversion(
stringify!($ty),
"number",
Some("out of range"),
));
}
}
Ok(())
}
}
impl_from_lua_integer!($ty);
)*
};
}
impl_lua_number_integer!(i8, u8, i16, u16, i32, u32);
impl_lua_wide_integer!(u64, i128, u128);
impl_lua_pointer_integer!(isize, usize);
macro_rules! impl_lua_float {
($($ty:ty),* $(,)?) => {
$(
impl<'lua> IntoLua<'lua> for $ty {
fn into_lua(self, _: LuaRef<'lua>) -> Result<Value<'lua>, Error> {
Ok(Value::Number(self as f64))
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe {
thread
.as_vm()
.push_number(self as f64)
.map_err(|exit| Error::from_thread_exit(thread, exit))?;
}
Ok(())
}
}
impl<'lua> FromLua<'lua> for $ty {
fn from_lua(value: Value<'lua>, lua: LuaRef<'lua>) -> Result<Self, Error> {
let from = value.type_name();
match value {
Value::Integer(value) => Ok(value as $ty),
Value::Number(value) => Ok(value as $ty),
Value::String(value) => coerce_string_to_number(&value, lua)?
.map(|value| value as $ty)
.ok_or_else(|| {
Error::from_lua_conversion(
from,
stringify!($ty),
Some("expected number or string coercible to number"),
)
}),
_ => Err(Error::from_lua_conversion(
from,
stringify!($ty),
Some("expected number or string coercible to number"),
)),
}
}
unsafe fn from_stack(thread: &Thread<'lua>, index: i32) -> Result<Self, Error> {
unsafe {
let vm_thread = thread.as_vm();
vm_thread
.to_integer64(index)
.map(|value| value as $ty)
.or_else(|| vm_thread.to_number(index).map(|value| value as $ty))
}
.ok_or_else(|| {
Error::from_lua_conversion(
thread.stack_type_name(index).as_str(),
stringify!($ty),
Some("expected number or string coercible to number"),
)
})
}
}
)*
};
}
impl_lua_float!(f32, f64);
impl<'lua, T> IntoLua<'lua> for Option<T>
where
T: IntoLua<'lua>,
{
fn into_lua(self, thread: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
match self {
Some(value) => value.into_lua(thread),
None => Ok(Value::Nil),
}
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
match self {
Some(value) => unsafe { value.push_into_stack(thread) },
None => Value::Nil.push_to(thread),
}
}
}
impl<'lua, T> FromLua<'lua> for Option<T>
where
T: FromLua<'lua>,
{
fn from_lua(value: Value<'lua>, thread: crate::LuaRef<'lua>) -> Result<Self, Error> {
match value {
Value::Nil => Ok(None),
value => T::from_lua(value, thread).map(Some),
}
}
}
impl<'lua> IntoLua<'lua> for Cow<'_, str> {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
match self {
Self::Borrowed(value) => value.into_lua(lua),
Self::Owned(value) => value.into_lua(lua),
}
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
match self {
Self::Borrowed(value) => unsafe { value.push_into_stack(thread) },
Self::Owned(value) => unsafe { value.push_into_stack(thread) },
}
}
}
impl<'lua> IntoLua<'lua> for Box<str> {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
self.as_ref().into_lua(lua)
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe { self.as_ref().push_into_stack(thread) }
}
}
impl<'lua> FromLua<'lua> for Box<str> {
fn from_lua(value: Value<'lua>, lua: crate::LuaRef<'lua>) -> Result<Self, Error> {
String::from_lua(value, lua).map(String::into_boxed_str)
}
}
impl<'lua> IntoLua<'lua> for CString {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
self.as_c_str().into_lua(lua)
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe { self.as_c_str().push_into_stack(thread) }
}
}
impl<'lua> FromLua<'lua> for CString {
fn from_lua(value: Value<'lua>, lua: crate::LuaRef<'lua>) -> Result<Self, Error> {
let from = value.type_name();
let string = LuaString::from_lua(value, lua)?;
CString::new(string.as_bytes().to_vec()).map_err(|error| {
let message = error.to_string();
Error::from_lua_conversion(from, core::any::type_name::<Self>(), Some(&message))
})
}
}
impl<'lua> IntoLua<'lua> for &CStr {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
self.to_bytes().into_lua(lua)
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe { self.to_bytes().push_into_stack(thread) }
}
}
impl<'lua> IntoLua<'lua> for Cow<'_, CStr> {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
match self {
Self::Borrowed(value) => value.into_lua(lua),
Self::Owned(value) => value.into_lua(lua),
}
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
match self {
Self::Borrowed(value) => unsafe { value.push_into_stack(thread) },
Self::Owned(value) => unsafe { value.push_into_stack(thread) },
}
}
}
impl<'lua> IntoLua<'lua> for OsString {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
self.as_os_str().into_lua(lua)
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe { self.as_os_str().push_into_stack(thread) }
}
}
impl<'lua> FromLua<'lua> for OsString {
fn from_lua(value: Value<'lua>, lua: crate::LuaRef<'lua>) -> Result<Self, Error> {
let from = value.type_name();
let bytes = BString::from_lua(value, lua)?;
Vec::from(bytes).into_os_string().map_err(|error| {
let message = error.to_string();
Error::from_lua_conversion(from, core::any::type_name::<Self>(), Some(&message))
})
}
}
impl<'lua> IntoLua<'lua> for &OsStr {
#[cfg(unix)]
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
use std::os::unix::ffi::OsStrExt;
self.as_bytes().into_lua(lua)
}
#[cfg(not(unix))]
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
self.to_string_lossy().into_lua(lua)
}
#[cfg(unix)]
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
use std::os::unix::ffi::OsStrExt;
unsafe { self.as_bytes().push_into_stack(thread) }
}
#[cfg(not(unix))]
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe { self.to_string_lossy().push_into_stack(thread) }
}
}
impl<'lua> IntoLua<'lua> for PathBuf {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
self.as_path().into_lua(lua)
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe { self.as_path().push_into_stack(thread) }
}
}
impl<'lua> FromLua<'lua> for PathBuf {
fn from_lua(value: Value<'lua>, lua: crate::LuaRef<'lua>) -> Result<Self, Error> {
OsString::from_lua(value, lua).map(Self::from)
}
}
impl<'lua> IntoLua<'lua> for &Path {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
self.as_os_str().into_lua(lua)
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe { self.as_os_str().push_into_stack(thread) }
}
}
impl<'lua> IntoLua<'lua> for char {
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
self.to_string().into_lua(lua)
}
unsafe fn push_into_stack(self, thread: &Thread<'lua>) -> Result<(), Error> {
unsafe { self.to_string().push_into_stack(thread) }
}
}
impl<'lua> FromLua<'lua> for char {
fn from_lua(value: Value<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
let from = value.type_name();
match value {
Value::Integer(value) => u32::try_from(value)
.ok()
.and_then(char::from_u32)
.ok_or_else(|| {
Error::from_lua_conversion(from, "char", Some("integer out of range"))
}),
Value::String(value) => {
let value = value.to_str()?;
let mut chars = value.chars();
match (chars.next(), chars.next()) {
(Some(value), None) => Ok(value),
_ => Err(Error::from_lua_conversion(
from,
"char",
Some("expected exactly one character"),
)),
}
}
_ => Err(Error::from_lua_conversion(
from,
"char",
Some("expected string or integer"),
)),
}
}
}
impl<'lua, T, const N: usize> IntoLua<'lua> for [T; N]
where
T: IntoLua<'lua>,
{
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
lua.create_sequence_from(self).map(Value::Table)
}
}
impl<'lua, T, const N: usize> FromLua<'lua> for [T; N]
where
T: FromLua<'lua>,
{
fn from_lua(value: Value<'lua>, lua: crate::LuaRef<'lua>) -> Result<Self, Error> {
if let Value::Vector(vector) = value {
if N != crate::Vector::SIZE {
let message = format!("expected vector of length {N}, got {}", crate::Vector::SIZE);
return Err(Error::from_lua_conversion(
"vector",
core::any::type_name::<Self>(),
Some(message.as_str()),
));
}
let x = T::from_lua(Value::Number(f64::from(vector.x())), lua)?;
let y = T::from_lua(Value::Number(f64::from(vector.y())), lua)?;
let z = T::from_lua(Value::Number(f64::from(vector.z())), lua)?;
#[cfg(feature = "vector4")]
let w = T::from_lua(Value::Number(f64::from(vector.w())), lua)?;
let mut array: [core::mem::MaybeUninit<T>; N] =
[const { core::mem::MaybeUninit::uninit() }; N];
array[0].write(x);
array[1].write(y);
array[2].write(z);
#[cfg(feature = "vector4")]
array[3].write(w);
return Ok(array.map(|value| unsafe { value.assume_init() }));
}
let from = value.type_name();
let Value::Table(table) = value else {
let message = format!("expected table of length {N}");
return Err(Error::from_lua_conversion(
from,
core::any::type_name::<Self>(),
Some(message.as_str()),
));
};
let values = table.sequence_values().collect::<Result<Vec<_>, _>>()?;
values.try_into().map_err(|values: Vec<T>| {
let message = format!("expected table of length {N}, got {}", values.len());
Error::from_lua_conversion(
"table",
core::any::type_name::<Self>(),
Some(message.as_str()),
)
})
}
}
impl<'lua, T> IntoLua<'lua> for Box<[T]>
where
T: IntoLua<'lua>,
{
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
lua.create_sequence_from(self.into_vec()).map(Value::Table)
}
}
impl<'lua, T> FromLua<'lua> for Box<[T]>
where
T: FromLua<'lua>,
{
fn from_lua(value: Value<'lua>, lua: crate::LuaRef<'lua>) -> Result<Self, Error> {
Vec::from_lua(value, lua).map(Vec::into_boxed_slice)
}
}
impl<'lua, T> IntoLua<'lua> for Vec<T>
where
T: IntoLua<'lua>,
{
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
lua.create_sequence_from(self).map(Value::Table)
}
}
impl<'lua, T> FromLua<'lua> for Vec<T>
where
T: FromLua<'lua>,
{
fn from_lua(value: Value<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
match value {
Value::Table(table) => table.sequence_values().collect(),
value => Err(Error::from_lua_conversion(
value.type_name(),
core::any::type_name::<Self>(),
Some("expected table"),
)),
}
}
}
impl<'lua, K, V, S> IntoLua<'lua> for HashMap<K, V, S>
where
K: Eq + Hash + IntoLua<'lua>,
V: IntoLua<'lua>,
S: BuildHasher,
{
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
lua.create_table_from(self).map(Value::Table)
}
}
impl<'lua, K, V, S> FromLua<'lua> for HashMap<K, V, S>
where
K: Eq + Hash + FromLua<'lua>,
V: FromLua<'lua>,
S: BuildHasher + Default,
{
fn from_lua(value: Value<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
match value {
Value::Table(table) => table.pairs().collect(),
value => Err(Error::from_lua_conversion(
value.type_name(),
core::any::type_name::<Self>(),
Some("expected table"),
)),
}
}
}
impl<'lua, K, V> IntoLua<'lua> for BTreeMap<K, V>
where
K: Ord + IntoLua<'lua>,
V: IntoLua<'lua>,
{
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
lua.create_table_from(self).map(Value::Table)
}
}
impl<'lua, K, V> FromLua<'lua> for BTreeMap<K, V>
where
K: Ord + FromLua<'lua>,
V: FromLua<'lua>,
{
fn from_lua(value: Value<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
match value {
Value::Table(table) => table.pairs().collect(),
value => Err(Error::from_lua_conversion(
value.type_name(),
core::any::type_name::<Self>(),
Some("expected table"),
)),
}
}
}
impl<'lua, T, S> IntoLua<'lua> for HashSet<T, S>
where
T: Eq + Hash + IntoLua<'lua>,
S: BuildHasher,
{
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
lua.create_table_from(self.into_iter().map(|value| (value, true)))
.map(Value::Table)
}
}
impl<'lua, T, S> FromLua<'lua> for HashSet<T, S>
where
T: Eq + Hash + FromLua<'lua>,
S: BuildHasher + Default,
{
fn from_lua(value: Value<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
match value {
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|value| value.map(|(key, _)| key))
.collect(),
value => Err(Error::from_lua_conversion(
value.type_name(),
core::any::type_name::<Self>(),
Some("expected table"),
)),
}
}
}
impl<'lua, T> IntoLua<'lua> for BTreeSet<T>
where
T: Ord + IntoLua<'lua>,
{
fn into_lua(self, lua: crate::LuaRef<'lua>) -> Result<Value<'lua>, Error> {
lua.create_table_from(self.into_iter().map(|value| (value, true)))
.map(Value::Table)
}
}
impl<'lua, T> FromLua<'lua> for BTreeSet<T>
where
T: Ord + FromLua<'lua>,
{
fn from_lua(value: Value<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
match value {
Value::Table(table) if table.raw_len() > 0 => table.sequence_values().collect(),
Value::Table(table) => table
.pairs::<T, Value<'lua>>()
.map(|value| value.map(|(key, _)| key))
.collect(),
value => Err(Error::from_lua_conversion(
value.type_name(),
core::any::type_name::<Self>(),
Some("expected table"),
)),
}
}
}
impl<'lua> IntoLuaMulti<'lua> for () {
fn into_lua_multi(self, _: crate::LuaRef<'lua>) -> Result<MultiValue<'lua>, Error> {
Ok(MultiValue::new())
}
unsafe fn push_into_stack_multi(self, _: &Thread<'lua>) -> Result<usize, Error> {
Ok(0)
}
}
impl<'lua> FromLuaMulti<'lua> for () {
fn from_lua_multi(_: MultiValue<'lua>, _: crate::LuaRef<'lua>) -> Result<Self, Error> {
Ok(())
}
unsafe fn from_stack_multi(_: &Thread<'lua>, _: i32, _: i32) -> Result<Self, Error> {
Ok(())
}
}
impl<'lua, T> IntoLuaMulti<'lua> for T
where
T: IntoLua<'lua>,
{
fn into_lua_multi(self, thread: crate::LuaRef<'lua>) -> Result<MultiValue<'lua>, Error> {
let mut values = MultiValue::with_capacity(1);
values.push(self.into_lua(thread)?);
Ok(values)
}
unsafe fn push_into_stack_multi(self, thread: &Thread<'lua>) -> Result<usize, Error> {
thread.reserve_stack(1)?;
unsafe {
self.push_into_stack(thread)?;
}
Ok(1)
}
}
impl<'lua, T, E> IntoLuaMulti<'lua> for Result<T, E>
where
T: IntoLuaMulti<'lua>,
E: IntoLua<'lua>,
{
fn into_lua_multi(self, thread: crate::LuaRef<'lua>) -> Result<MultiValue<'lua>, Error> {
match self {
Ok(value) => value.into_lua_multi(thread),
Err(error) => {
let mut values = MultiValue::with_capacity(2);
values.push(Value::Nil);
values.push(error.into_lua(thread)?);
Ok(values)
}
}
}
unsafe fn push_into_stack_multi(self, thread: &Thread<'lua>) -> Result<usize, Error> {
match self {
Ok(value) => unsafe { value.push_into_stack_multi(thread) },
Err(error) => {
thread.reserve_stack(2)?;
Value::Nil.push_to(thread)?;
unsafe {
error.push_into_stack(thread)?;
}
Ok(2)
}
}
}
}
impl<'lua, T> FromLuaMulti<'lua> for T
where
T: FromLua<'lua>,
{
fn from_lua_multi(
mut values: MultiValue<'lua>,
thread: crate::LuaRef<'lua>,
) -> Result<Self, Error> {
T::from_lua(values.pop_front().unwrap_or(Value::Nil), thread)
}
unsafe fn from_stack_multi(
thread: &Thread<'lua>,
base_top: i32,
count: i32,
) -> Result<Self, Error> {
if count == 0 {
return T::from_lua(Value::Nil, thread.lua_ref());
}
unsafe { T::from_stack(thread, base_top + 1) }
}
}
macro_rules! impl_tuple {
($fixed_count:expr, $(($name:ident $index:tt),)* @ ($last:ident $last_index:tt)) => {
impl<'lua, $($name,)* $last> IntoLuaMulti<'lua> for ($($name,)* $last,)
where
$($name: IntoLua<'lua>,)*
$last: IntoLuaMulti<'lua>,
{
fn into_lua_multi(self, thread: crate::LuaRef<'lua>) -> Result<MultiValue<'lua>, Error> {
let mut values = MultiValue::with_capacity($fixed_count);
$(values.push(self.$index.into_lua(thread)?);)*
for value in self.$last_index.into_lua_multi(thread)? {
values.push(value);
}
Ok(values)
}
unsafe fn push_into_stack_multi(self, thread: &Thread<'lua>) -> Result<usize, Error> {
thread.reserve_stack($fixed_count)?;
$(unsafe { self.$index.push_into_stack(thread)?; })*
let tail = unsafe { self.$last_index.push_into_stack_multi(thread)? };
($fixed_count as usize)
.checked_add(tail)
.ok_or(Error::StackError)
}
}
impl<'lua, $($name,)* $last> FromLuaMulti<'lua> for ($($name,)* $last,)
where
$($name: FromLua<'lua>,)*
$last: FromLuaMulti<'lua>,
{
#[allow(non_snake_case, unused_mut)]
fn from_lua_multi(mut values: MultiValue<'lua>, thread: crate::LuaRef<'lua>) -> Result<Self, Error> {
$(let $name = $name::from_lua(values.pop_front().unwrap_or(Value::Nil), thread)?;)*
let $last = $last::from_lua_multi(values, thread)?;
Ok(($($name,)* $last,))
}
#[allow(non_snake_case)]
unsafe fn from_stack_multi(
thread: &Thread<'lua>,
base_top: i32,
count: i32,
) -> Result<Self, Error> {
$(let $name = if ($index as i32) < count {
unsafe { $name::from_stack(thread, base_top + $index + 1)? }
} else {
$name::from_lua(Value::Nil, thread.lua_ref())?
};)*
let $last = unsafe {
$last::from_stack_multi(
thread,
base_top + $fixed_count,
count.saturating_sub($fixed_count),
)?
};
Ok(($($name,)* $last,))
}
}
};
}
impl_tuple!(0, @ (A 0));
impl_tuple!(1, (A 0), @ (B 1));
impl_tuple!(2, (A 0), (B 1), @ (C 2));
impl_tuple!(3, (A 0), (B 1), (C 2), @ (D 3));
impl_tuple!(4, (A 0), (B 1), (C 2), (D 3), @ (E 4));
impl_tuple!(5, (A 0), (B 1), (C 2), (D 3), (E 4), @ (F 5));
impl_tuple!(6, (A 0), (B 1), (C 2), (D 3), (E 4), (F 5), @ (G 6));
impl_tuple!(7, (A 0), (B 1), (C 2), (D 3), (E 4), (F 5), (G 6), @ (H 7));