#[doc(hidden)]
pub extern crate lua52_sys as ffi;
extern crate libc;
use std::ffi::{CStr, CString};
use std::io::Read;
use std::io::Error as IoError;
use std::borrow::Borrow;
use std::marker::PhantomData;
use std::error::Error;
use std::fmt;
use std::convert::From;
use std::io;
pub use any::{AnyHashableLuaValue, AnyLuaString, AnyLuaValue};
pub use functions_write::{Function, InsideCallback};
pub use functions_write::{function0, function1, function2, function3, function4, function5};
pub use functions_write::{function6, function7, function8, function9, function10};
pub use lua_functions::LuaFunction;
pub use lua_functions::LuaFunctionCallError;
pub use lua_functions::{LuaCode, LuaCodeFromReader};
pub use lua_tables::LuaTable;
pub use lua_tables::LuaTableIterator;
pub use tuples::TuplePushError;
pub use userdata::UserdataOnStack;
pub use userdata::{push_userdata, read_userdata};
pub use values::StringInLua;
mod any;
mod functions_write;
mod lua_functions;
mod lua_tables;
mod macros;
mod rust_tables;
mod userdata;
mod values;
mod tuples;
#[derive(Debug)]
pub struct Lua<'lua> {
lua: LuaContext,
must_be_closed: bool,
marker: PhantomData<&'lua ()>,
}
#[derive(Debug)]
pub struct PushGuard<L> {
lua: L,
size: i32,
raw_lua: LuaContext,
}
impl<'lua, L> PushGuard<L>
where L: AsMutLua<'lua>
{
#[inline]
pub unsafe fn new(mut lua: L, size: i32) -> Self {
let raw_lua = lua.as_mut_lua();
PushGuard {
lua,
size,
raw_lua,
}
}
#[inline]
fn assert_one_and_forget(self) -> i32 {
assert_eq!(self.size, 1);
self.forget_internal()
}
#[inline]
pub fn size(&self) -> i32 {
self.size
}
#[inline]
pub unsafe fn forget(self) -> i32 {
self.forget_internal()
}
#[inline]
fn forget_internal(mut self) -> i32 {
let size = self.size;
self.size = 0;
size
}
#[inline]
pub fn into_inner(mut self) -> L {
use std::{mem, ptr};
let mut res;
unsafe {
res = mem::uninitialized();
ptr::copy_nonoverlapping(&self.lua, &mut res, 1);
if self.size != 0 {
ffi::lua_pop(self.lua.as_mut_lua().0, self.size);
}
};
mem::forget(self);
res
}
}
pub unsafe trait AsLua<'lua> {
fn as_lua(&self) -> LuaContext;
}
pub unsafe trait AsMutLua<'lua>: AsLua<'lua> {
fn as_mut_lua(&mut self) -> LuaContext;
}
#[derive(Copy, Clone, Debug)]
pub struct LuaContext(*mut ffi::lua_State);
impl LuaContext {
#[doc(hidden)]
#[inline]
pub fn state_ptr(&self) -> *mut ffi::lua_State {
self.0
}
}
unsafe impl Send for LuaContext {}
unsafe impl<'a, 'lua> AsLua<'lua> for Lua<'lua> {
#[inline]
fn as_lua(&self) -> LuaContext {
self.lua
}
}
unsafe impl<'lua> AsMutLua<'lua> for Lua<'lua> {
#[inline]
fn as_mut_lua(&mut self) -> LuaContext {
self.lua
}
}
unsafe impl<'lua, L> AsLua<'lua> for PushGuard<L>
where L: AsMutLua<'lua>
{
#[inline]
fn as_lua(&self) -> LuaContext {
self.lua.as_lua()
}
}
unsafe impl<'lua, L> AsMutLua<'lua> for PushGuard<L>
where L: AsMutLua<'lua>
{
#[inline]
fn as_mut_lua(&mut self) -> LuaContext {
self.lua.as_mut_lua()
}
}
unsafe impl<'a, 'lua, L: ?Sized> AsLua<'lua> for &'a L
where L: AsLua<'lua>
{
#[inline]
fn as_lua(&self) -> LuaContext {
(**self).as_lua()
}
}
unsafe impl<'a, 'lua, L: ?Sized> AsLua<'lua> for &'a mut L
where L: AsLua<'lua>
{
#[inline]
fn as_lua(&self) -> LuaContext {
(**self).as_lua()
}
}
unsafe impl<'a, 'lua, L: ?Sized> AsMutLua<'lua> for &'a mut L
where L: AsMutLua<'lua>
{
#[inline]
fn as_mut_lua(&mut self) -> LuaContext {
(**self).as_mut_lua()
}
}
pub trait Push<L> {
type Err;
fn push_to_lua(self, lua: L) -> Result<PushGuard<L>, (Self::Err, L)>;
#[inline]
fn push_no_err<E>(self, lua: L) -> PushGuard<L>
where Self: Sized,
Self: Push<L, Err = E>,
E: Into<Void>,
{
match self.push_to_lua(lua) {
Ok(p) => p,
Err(_) => unreachable!(),
}
}
}
pub trait PushOne<L>: Push<L> {}
#[derive(Debug, Copy, Clone)]
pub enum Void {}
impl fmt::Display for Void {
fn fmt(&self, _f: &mut fmt::Formatter) -> fmt::Result {
unreachable!("Void cannot be instantiated")
}
}
pub trait LuaRead<L>: Sized {
#[inline]
fn lua_read(lua: L) -> Result<Self, L> {
LuaRead::lua_read_at_position(lua, -1)
}
fn lua_read_at_position(lua: L, index: i32) -> Result<Self, L>;
}
#[derive(Debug)]
pub enum LuaError {
SyntaxError(String),
ExecutionError(String),
ReadError(IoError),
WrongType,
}
impl fmt::Display for LuaError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use LuaError::*;
match *self {
SyntaxError(ref s) => write!(f, "Syntax error: {}", s),
ExecutionError(ref s) => write!(f, "Execution error: {}", s),
ReadError(ref e) => write!(f, "Read error: {}", e),
WrongType => write!(f, "Wrong type returned by Lua"),
}
}
}
impl Error for LuaError {
fn description(&self) -> &str {
use LuaError::*;
match *self {
SyntaxError(ref s) => &s,
ExecutionError(ref s) => &s,
ReadError(_) => "read error",
WrongType => "wrong type returned by Lua",
}
}
fn cause(&self) -> Option<&Error> {
use LuaError::*;
match *self {
SyntaxError(_) => None,
ExecutionError(_) => None,
ReadError(ref e) => Some(e),
WrongType => None,
}
}
}
impl From<io::Error> for LuaError {
fn from(e: io::Error) -> Self {
LuaError::ReadError(e)
}
}
impl<'lua> Lua<'lua> {
#[inline]
pub fn new() -> Lua<'lua> {
let lua = unsafe { ffi::lua_newstate(alloc, std::ptr::null_mut()) };
if lua.is_null() {
panic!("lua_newstate failed");
}
extern "C" fn alloc(_ud: *mut libc::c_void,
ptr: *mut libc::c_void,
_osize: libc::size_t,
nsize: libc::size_t)
-> *mut libc::c_void {
unsafe {
if nsize == 0 {
libc::free(ptr as *mut libc::c_void);
std::ptr::null_mut()
} else {
libc::realloc(ptr, nsize)
}
}
}
extern "C" fn panic(lua: *mut ffi::lua_State) -> libc::c_int {
let err = unsafe { ffi::lua_tostring(lua, -1) };
let err = unsafe { CStr::from_ptr(err) };
let err = String::from_utf8(err.to_bytes().to_vec()).unwrap();
panic!("PANIC: unprotected error in call to Lua API ({})\n", err);
}
unsafe { ffi::lua_atpanic(lua, panic) };
Lua {
lua: LuaContext(lua),
must_be_closed: true,
marker: PhantomData,
}
}
#[inline]
pub unsafe fn from_existing_state<T>(lua: *mut T, close_at_the_end: bool) -> Lua<'lua> {
Lua {
lua: std::mem::transmute(lua),
must_be_closed: close_at_the_end,
marker: PhantomData,
}
}
#[inline]
pub fn openlibs(&mut self) {
unsafe { ffi::luaL_openlibs(self.lua.0) }
}
#[inline]
pub fn open_base(&mut self) {
unsafe { ffi::luaopen_base(self.lua.0) }
}
#[inline]
pub fn open_bit32(&mut self) {
unsafe { ffi::luaopen_bit32(self.lua.0) }
}
#[inline]
pub fn open_coroutine(&mut self) {
unsafe { ffi::luaopen_coroutine(self.lua.0) }
}
#[inline]
pub fn open_debug(&mut self) {
unsafe { ffi::luaopen_debug(self.lua.0) }
}
#[inline]
pub fn open_io(&mut self) {
unsafe { ffi::luaopen_io(self.lua.0) }
}
#[inline]
pub fn open_math(&mut self) {
unsafe { ffi::luaopen_math(self.lua.0) }
}
#[inline]
pub fn open_os(&mut self) {
unsafe { ffi::luaopen_os(self.lua.0) }
}
#[inline]
pub fn open_package(&mut self) {
unsafe { ffi::luaopen_package(self.lua.0) }
}
#[inline]
pub fn open_string(&mut self) {
unsafe { ffi::luaopen_string(self.lua.0) }
}
#[inline]
pub fn open_table(&mut self) {
unsafe { ffi::luaopen_table(self.lua.0) }
}
#[inline]
pub fn execute<'a, T>(&'a mut self, code: &str) -> Result<T, LuaError>
where T: for<'g> LuaRead<PushGuard<&'g mut PushGuard<&'a mut Lua<'lua>>>>
{
let mut f = try!(lua_functions::LuaFunction::load(self, code));
f.call()
}
#[inline]
pub fn execute_from_reader<'a, T, R>(&'a mut self, code: R) -> Result<T, LuaError>
where T: for<'g> LuaRead<PushGuard<&'g mut PushGuard<&'a mut Lua<'lua>>>>,
R: Read
{
let mut f = try!(lua_functions::LuaFunction::load_from_reader(self, code));
f.call()
}
#[inline]
pub fn get<'l, V, I>(&'l mut self, index: I) -> Option<V>
where I: Borrow<str>,
V: LuaRead<PushGuard<&'l mut Lua<'lua>>>
{
let index = CString::new(index.borrow()).unwrap();
unsafe {
ffi::lua_getglobal(self.lua.0, index.as_ptr());
}
if unsafe { ffi::lua_isnil(self.as_lua().0, -1) } {
let raw_lua = self.as_lua();
let _guard = PushGuard {
lua: self,
size: 1,
raw_lua: raw_lua,
};
return None;
}
let raw_lua = self.as_lua();
let guard = PushGuard {
lua: self,
size: 1,
raw_lua: raw_lua,
};
LuaRead::lua_read(guard).ok()
}
#[inline]
pub fn into_get<V, I>(self, index: I) -> Result<V, PushGuard<Self>>
where I: Borrow<str>,
V: LuaRead<PushGuard<Lua<'lua>>>
{
let index = CString::new(index.borrow()).unwrap();
unsafe {
ffi::lua_getglobal(self.lua.0, index.as_ptr());
}
let is_nil = unsafe { ffi::lua_isnil(self.as_lua().0, -1) };
let raw_lua = self.as_lua();
let guard = PushGuard {
lua: self,
size: 1,
raw_lua: raw_lua,
};
if is_nil {
Err(guard)
} else {
LuaRead::lua_read(guard)
}
}
#[inline]
pub fn set<I, V, E>(&mut self, index: I, value: V)
where I: Borrow<str>,
for<'a> V: PushOne<&'a mut Lua<'lua>, Err = E>,
E: Into<Void>,
{
match self.checked_set(index, value) {
Ok(_) => (),
Err(_) => unreachable!(),
}
}
#[inline]
pub fn checked_set<I, V, E>(&mut self, index: I, value: V) -> Result<(), E>
where I: Borrow<str>,
for<'a> V: PushOne<&'a mut Lua<'lua>, Err = E>
{
unsafe {
let mut me = self;
ffi::lua_pushglobaltable(me.lua.0);
match index.borrow().push_to_lua(&mut me) {
Ok(pushed) => {
debug_assert_eq!(pushed.size, 1);
pushed.forget()
}
Err(_) => unreachable!(),
};
match value.push_to_lua(&mut me) {
Ok(pushed) => {
assert_eq!(pushed.size, 1);
pushed.forget()
}
Err((err, lua)) => {
ffi::lua_pop(lua.lua.0, 2);
return Err(err);
}
};
ffi::lua_settable(me.lua.0, -3);
ffi::lua_pop(me.lua.0, 1);
Ok(())
}
}
#[inline]
pub fn empty_array<'a, I>(&'a mut self, index: I) -> LuaTable<PushGuard<&'a mut Lua<'lua>>>
where I: Borrow<str>
{
unsafe {
let mut me = self;
ffi::lua_pushglobaltable(me.lua.0);
match index.borrow().push_to_lua(&mut me) {
Ok(pushed) => pushed.forget(),
Err(_) => unreachable!(),
};
ffi::lua_newtable(me.lua.0);
ffi::lua_settable(me.lua.0, -3);
ffi::lua_pop(me.lua.0, 1);
me.get(index).unwrap()
}
}
#[inline]
pub fn globals_table<'a>(&'a mut self) -> LuaTable<PushGuard<&'a mut Lua<'lua>>> {
unsafe {
ffi::lua_pushglobaltable(self.lua.0);
}
let raw_lua = self.as_lua();
let guard = PushGuard {
lua: self,
size: 1,
raw_lua: raw_lua,
};
LuaRead::lua_read(guard).ok().unwrap()
}
}
impl<'lua> Drop for Lua<'lua> {
#[inline]
fn drop(&mut self) {
if self.must_be_closed {
unsafe { ffi::lua_close(self.lua.0) }
}
}
}
impl<L> Drop for PushGuard<L> {
#[inline]
fn drop(&mut self) {
if self.size != 0 {
unsafe {
ffi::lua_pop(self.raw_lua.0, self.size);
}
}
}
}
#[cfg(test)]
mod tests {
use Lua;
use LuaError;
#[test]
fn open_base_opens_base_library() {
let mut lua = Lua::new();
match lua.execute::<()>("return assert(true)") {
Err(LuaError::ExecutionError(_)) => { },
Err(_) => panic!("Wrong error"),
Ok(_) => panic!("Unexpected success"),
}
lua.open_base();
let result: bool = lua.execute("return assert(true)").unwrap();
assert_eq!(result, true);
}
#[test]
fn opening_all_libraries_doesnt_panic() {
let mut lua = Lua::new();
lua.open_base();
lua.open_bit32();
lua.open_coroutine();
lua.open_debug();
lua.open_io();
lua.open_math();
lua.open_os();
lua.open_package();
lua.open_string();
lua.open_table();
}
}