pub extern crate lua;
#[macro_export]
macro_rules! auto_cleanup {
($state:ident, $b:block) => {{
let top = $state.get_top();
let result = $b;
$state.set_top(top);
result
}};
}
#[macro_export]
macro_rules! convert_arguments {
(@strict $strict:expr, $state:ident, $($from:tt),+) => {{
use $crate::lua::Index;
let names = [$(stringify!($from),)+];
let quantity = names.len() as Index;
let top = $state.get_top();
auto_cleanup!($state, {
let mut collect = || {
let base = $state.get_top() - quantity;
if base < 0 {
return Err(top + 1); }
if $strict && base > 0 {
return Err(top);
}
let mut position = 0;
let result = ($({
position += 1;
convert_arguments!(@unpack $from, $state, base, position)
},)+);
Ok(result)
};
collect()
})
}};
(@unpack _, $state:ident, $base:expr, $position:expr) => {()};
(@unpack $from:ty, $state:ident, $base:expr, $position:expr) => {{
let opt = $state.to_type::<$from>($base + $position);
match opt {
Some(v) => v,
None => {
return Err($position);
},
}
}};
($state:ident, $($from:tt),+) =>
(convert_arguments!(@strict true, $state, $($from),+));
}
#[macro_export]
macro_rules! lua_table_type {
($name:ident < $key:ty , $val:ty >) => {
pub struct $name(pub ::std::collections::HashMap<$key, $val>);
impl $crate::lua::FromLua for $name {
fn from_lua(state: &mut $crate::lua::State, index: $crate::lua::Index) -> Option<Self> {
if !state.is_table(index) {
return None;
}
let mut map = ::std::collections::HashMap::new();
let index = state.abs_index(index);
state.push_nil();
while state.next(index) {
if let Ok((name, value)) = convert_arguments!(@strict false, state, $key, $val) {
map.insert(name, value);
state.pop(1); } else {
state.pop(2); return None;
}
}
Some($name(map))
}
}
};
}
#[macro_export]
macro_rules! lua_array_type {
($name:ident < $val:ty >) => {
pub struct $name(pub ::std::vec::Vec<$val>);
impl $crate::lua::FromLua for $name {
fn from_lua(state: &mut $crate::lua::State, index: $crate::lua::Index) -> Option<Self> {
if !state.is_table(index) {
return None;
}
let mut vec = ::std::vec::Vec::new();
let index = state.abs_index(index);
for idx in 1.. {
state.geti(index, idx);
if state.is_nil(-1) {
state.pop(1);
break;
}
if let Ok((value,)) = convert_arguments!(@strict false, state, $val) {
vec.push(value);
state.pop(1);
} else {
state.pop(1);
return None;
}
}
Some($name(vec))
}
}
impl $crate::lua::ToLua for $name {
fn to_lua(&self, state: &mut $crate::lua::State) {
let $name(ref vec) = *self;
state.new_table();
let mut idx = 0;
for item in vec {
idx += 1; item.to_lua(state);
state.raw_seti(-2, idx);
}
}
}
};
}
#[macro_export]
macro_rules! lua_userdata {
($ud:ident $(, $field:expr => $func:ident )*) => {
impl $ud {
pub fn meta_name() -> &'static str {
concat!(stringify!($ud), ".Rust")
}
pub fn attach(state: &mut State) {
let created = state.new_metatable($ud::meta_name());
$(
state.push_fn(Some($func));
state.set_field(-2, $field);
)*
state.push_fn(Some($ud::drop_it));
state.set_field(-2, "__gc");
state.pop(1); if !created {
panic!("Metatable '{}' already exists.", $ud::meta_name());
}
}
unsafe extern "C" fn drop_it(state: *mut $crate::lua::ffi::lua_State) -> i32 {
let mut state = $crate::lua::State::from_ptr(state);
if let Some(ptr) = state.to_userdata_typed::<$ud>(-1) {
::std::ptr::drop_in_place(ptr);
}
0
}
}
impl $crate::lua::FromLua for $ud {
fn from_lua(state: &mut $crate::lua::State, index: $crate::lua::Index) -> Option<Self> {
unsafe {
state.test_userdata_typed::<$ud>(index, $ud::meta_name())
.map(|p| p.clone())
}
}
}
impl $crate::lua::ToLua for $ud {
fn to_lua(&self, state: &mut $crate::lua::State) {
unsafe {
let pointer = state.new_userdata_typed();
let uninit = ::std::ptr::replace(pointer, self.clone());
::std::mem::forget(uninit);
}
state.set_metatable_from_registry($ud::meta_name());
}
}
};
}