use crate::lua_value::LuaValue;
use crate::lua_vm::LuaState;
pub(crate) fn collect_into_lua_values<T: IntoLua>(
state: &mut LuaState,
value: T,
) -> Result<Vec<LuaValue>, String> {
let base_top = state.get_top();
let pushed = match value.into_lua(state) {
Ok(pushed) => pushed,
Err(err) => {
state.set_top_raw(base_top);
return Err(err);
}
};
let mut values = Vec::with_capacity(pushed);
for index in base_top..base_top + pushed {
let Some(value) = state.stack_get(index) else {
state.set_top_raw(base_top);
return Err("internal error: failed to collect Lua values from stack".to_owned());
};
values.push(value);
}
state.set_top_raw(base_top);
Ok(values)
}
pub trait FromLua: Sized {
fn from_lua(value: LuaValue, state: &LuaState) -> Result<Self, String>;
}
pub trait FromLuaMulti: Sized {
fn from_lua_multi(values: Vec<LuaValue>, state: &LuaState) -> Result<Self, String>;
}
pub trait IntoLua {
fn into_lua(self, state: &mut LuaState) -> Result<usize, String>;
}
impl FromLua for LuaValue {
#[inline]
fn from_lua(value: LuaValue, _state: &LuaState) -> Result<Self, String> {
Ok(value)
}
}
impl IntoLua for LuaValue {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
state.push_value(self).map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
impl FromLua for () {
#[inline]
fn from_lua(_value: LuaValue, _state: &LuaState) -> Result<Self, String> {
Ok(())
}
}
impl IntoLua for () {
#[inline]
fn into_lua(self, _state: &mut LuaState) -> Result<usize, String> {
Ok(0)
}
}
impl<T: FromLua> FromLuaMulti for T {
#[inline]
fn from_lua_multi(values: Vec<LuaValue>, state: &LuaState) -> Result<Self, String> {
let value = values.into_iter().next().unwrap_or_default();
T::from_lua(value, state)
}
}
impl FromLuaMulti for Vec<LuaValue> {
#[inline]
fn from_lua_multi(values: Vec<LuaValue>, _state: &LuaState) -> Result<Self, String> {
Ok(values)
}
}
impl FromLua for bool {
#[inline]
fn from_lua(value: LuaValue, _state: &LuaState) -> Result<Self, String> {
Ok(value.as_boolean().unwrap_or(!value.is_nil()))
}
}
impl IntoLua for bool {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
state
.push_value(LuaValue::boolean(self))
.map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
macro_rules! impl_from_lua_int {
($($ty:ty),*) => {
$(
impl FromLua for $ty {
#[inline]
fn from_lua(value: LuaValue, _state: &LuaState) -> Result<Self, String> {
if let Some(i) = value.as_integer() {
Ok(i as $ty)
} else if let Some(f) = value.as_float() {
Ok(f as $ty)
} else {
Err(format!("expected integer, got {}", value.type_name()))
}
}
}
impl IntoLua for $ty {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
state
.push_value(LuaValue::integer(self as i64))
.map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
)*
};
}
impl_from_lua_int!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
macro_rules! impl_from_lua_float {
($($ty:ty),*) => {
$(
impl FromLua for $ty {
#[inline]
fn from_lua(value: LuaValue, _state: &LuaState) -> Result<Self, String> {
if let Some(n) = value.as_number() {
Ok(n as $ty)
} else if let Some(i) = value.as_integer() {
Ok(i as $ty)
} else {
Err(format!("expected number, got {}", value.type_name()))
}
}
}
impl IntoLua for $ty {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
state
.push_value(LuaValue::float(self as f64))
.map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
)*
};
}
impl_from_lua_float!(f32, f64);
impl FromLua for String {
#[inline]
fn from_lua(value: LuaValue, _state: &LuaState) -> Result<Self, String> {
if let Some(s) = value.as_str() {
Ok(s.to_owned())
} else if let Some(i) = value.as_integer() {
Ok(format!("{}", i))
} else if let Some(f) = value.as_float() {
Ok(format!("{}", f))
} else {
Err(format!("expected string, got {}", value.type_name()))
}
}
}
impl IntoLua for String {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
let s = state.create_string(&self).map_err(|e| format!("{:?}", e))?;
state.push_value(s).map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
impl IntoLua for &str {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
let s = state.create_string(self).map_err(|e| format!("{:?}", e))?;
state.push_value(s).map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
impl<T: FromLua> FromLua for Option<T> {
#[inline]
fn from_lua(value: LuaValue, state: &LuaState) -> Result<Self, String> {
if value.is_nil() {
Ok(None)
} else {
T::from_lua(value, state).map(Some)
}
}
}
impl<T: IntoLua> IntoLua for Option<T> {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
match self {
Some(v) => v.into_lua(state),
None => {
state
.push_value(LuaValue::nil())
.map_err(|e| format!("{:?}", e))?;
Ok(1)
}
}
}
}
impl<T: IntoLua, E: std::fmt::Display> IntoLua for Result<T, E> {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
match self {
Ok(v) => v.into_lua(state),
Err(e) => Err(format!("{}", e)),
}
}
}
impl<T: IntoLua> IntoLua for Vec<T> {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
let count = self.len();
for item in self {
item.into_lua(state)?;
}
Ok(count)
}
}
macro_rules! impl_lua_tuple_conversions {
($(($(($ty:ident, $value:ident)),+)),* $(,)?) => {
$(
impl<$($ty: IntoLua),+> IntoLua for ($($ty,)+) {
#[inline]
fn into_lua(self, state: &mut LuaState) -> Result<usize, String> {
let ($($value,)+) = self;
let mut pushed = 0;
$(
pushed += $value.into_lua(state)?;
)+
Ok(pushed)
}
}
impl<$($ty: FromLua),+> FromLuaMulti for ($($ty,)+) {
#[inline]
fn from_lua_multi(values: Vec<LuaValue>, state: &LuaState) -> Result<Self, String> {
let mut iter = values.into_iter();
Ok(($(
$ty::from_lua(iter.next().unwrap_or(LuaValue::nil()), state)?,
)+))
}
}
)*
};
}
impl_lua_tuple_conversions!(
((A, a), (B, b)),
((A, a), (B, b), (C, c)),
((A, a), (B, b), (C, c), (D, d)),
((A, a), (B, b), (C, c), (D, d), (E, e)),
((A, a), (B, b), (C, c), (D, d), (E, e), (F, f)),
((A, a), (B, b), (C, c), (D, d), (E, e), (F, f), (G, g)),
(
(A, a),
(B, b),
(C, c),
(D, d),
(E, e),
(F, f),
(G, g),
(H, h)
)
);