Working
This commit is contained in:
@@ -82,6 +82,9 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
/// Lua result.
|
||||
pub type Result<T> = ::std::result::Result<T, Error>;
|
||||
|
||||
/// Lua type.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub enum Type {
|
||||
@@ -306,28 +309,37 @@ impl Default for DumpMode {
|
||||
pub struct Chunk {
|
||||
name: BString,
|
||||
content: BString,
|
||||
mode: LoadMode,
|
||||
}
|
||||
|
||||
impl Chunk {
|
||||
/// Creates a named [`Chunk`] with the given content.
|
||||
pub fn named(name: impl Into<BString>, content: impl Into<BString>) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
content: content.into(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates an unnamed [`Chunk`] with the given content.
|
||||
pub fn unnamed(content: impl Into<BString>) -> Self {
|
||||
/// Creates a new [`Chunk`] with the given content.
|
||||
pub fn new(content: impl Into<BString>) -> Self {
|
||||
Self {
|
||||
name: "?".into(),
|
||||
content: content.into(),
|
||||
mode: LoadMode::AUTO,
|
||||
}
|
||||
}
|
||||
|
||||
/// Name of this chunk.
|
||||
pub fn name(&self) -> &BStr {
|
||||
self.name.as_ref()
|
||||
/// Sets the name of this chunk as `name`.
|
||||
pub fn name(&mut self, name: impl AsRef<[u8]>) -> &mut Self {
|
||||
self.name = name.as_ref().into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the name of this chunk as the path `path`.
|
||||
pub fn path(&mut self, path: impl AsRef<[u8]>) -> &mut Self {
|
||||
let mut name = BString::from(b"@");
|
||||
name.extend_from_slice(path.as_ref());
|
||||
self.name = name;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the mode flag for loading this chunk.
|
||||
pub fn mode(&mut self, mode: LoadMode) -> &mut Self {
|
||||
self.mode = mode;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,13 +357,19 @@ impl DerefMut for Chunk {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<BString>> From<T> for Chunk {
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GlobalState {
|
||||
ptr: NonNull<lua_State>,
|
||||
}
|
||||
|
||||
impl GlobalState {
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
pub fn new() -> Result<Self> {
|
||||
unsafe {
|
||||
// SAFETY: lua_newstate may return a null pointer if allocation fails
|
||||
let ptr = NonNull::new(lua_newstate(Some(Self::alloc_cb), ptr::null_mut()))
|
||||
@@ -455,7 +473,7 @@ impl Drop for Ref {
|
||||
/// A state instance can be manipulated using the [`Stack`] object that it mutably dereferences to.
|
||||
#[derive(Debug)]
|
||||
pub struct State {
|
||||
thread_ref: Ref,
|
||||
thread: Ref,
|
||||
stack: Stack,
|
||||
}
|
||||
|
||||
@@ -465,11 +483,11 @@ impl State {
|
||||
/// All built-in libraries are opened by default.
|
||||
///
|
||||
/// This may return an error if allocation or library initialisation fails.
|
||||
pub fn new() -> Result<Self, Error> {
|
||||
pub fn new() -> Result<Self> {
|
||||
let state = Rc::new(GlobalState::new()?);
|
||||
let mut state = Self {
|
||||
stack: unsafe { Stack::new_unchecked(state.as_ptr()) },
|
||||
thread_ref: Ref {
|
||||
thread: Ref {
|
||||
state,
|
||||
key: LUA_NOREF,
|
||||
},
|
||||
@@ -492,12 +510,25 @@ impl State {
|
||||
Self {
|
||||
// SAFETY: lua_newthread never returns null, but may panic on oom
|
||||
stack: unsafe { Stack::new_unchecked(lua_newthread(self.as_ptr())) },
|
||||
thread_ref: Ref {
|
||||
state: Rc::clone(&self.thread_ref.state),
|
||||
thread: Ref {
|
||||
state: Rc::clone(&self.thread.state),
|
||||
key: unsafe { luaL_ref(self.as_ptr(), LUA_REGISTRYINDEX) },
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`Ref`] with the given key.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that the given ref key is unique and not already used by any other
|
||||
/// instances of [`Ref`].
|
||||
pub unsafe fn new_ref_unchecked(&self, key: c_int) -> Ref {
|
||||
Ref {
|
||||
state: Rc::clone(&self.thread.state),
|
||||
key,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for State {
|
||||
@@ -616,6 +647,23 @@ impl Stack {
|
||||
unsafe { lua_pop(self.as_ptr(), n) }
|
||||
}
|
||||
|
||||
/// Pops the value at the top of the stack and inserts it at index `idx` by shifting up existing
|
||||
/// values.
|
||||
///
|
||||
/// Index `idx` cannot be a pseudo-index.
|
||||
///
|
||||
/// Equivalent to [`lua_insert`].
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if the stack is empty or the index `idx` is invalid.
|
||||
pub fn pop_insert(&mut self, idx: c_int) {
|
||||
assert!(self.size() >= 1, "cannot pop 1: {self:?}");
|
||||
let idx = self.slot(idx).index();
|
||||
assert!(idx > 0, "cannot insert into pseudo-index {idx}");
|
||||
unsafe { lua_insert(self.as_ptr(), idx) }
|
||||
}
|
||||
|
||||
/// Pops the value at the top of the stack and replaces the value at index `idx` with it.
|
||||
///
|
||||
/// If the index `idx` points to the top of the stack, this still pops the value and is
|
||||
@@ -800,11 +848,11 @@ impl Stack {
|
||||
/// Pushes the given chunk as a function at the top of the stack.
|
||||
///
|
||||
/// Equivalent to [`lua_loadx`].
|
||||
pub fn load(&mut self, chunk: &Chunk, mode: LoadMode) -> Result<(), Error> {
|
||||
pub fn load(&mut self, chunk: &Chunk) -> Result<()> {
|
||||
type State<'s> = Option<&'s [u8]>;
|
||||
let mut state: State = Some(chunk.content.as_ref());
|
||||
let name = CString::new(chunk.name.to_vec()).map_err(Error::BadChunkName)?;
|
||||
let mode = mode.to_mode_str();
|
||||
let mode = chunk.mode.to_mode_str();
|
||||
|
||||
unsafe extern "C" fn reader_cb(
|
||||
_L: *mut lua_State,
|
||||
@@ -860,7 +908,7 @@ impl Stack {
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if the value at index `idx` is not a function.
|
||||
pub fn dump(&self, idx: c_int, mode: DumpMode) -> Result<BString, Error> {
|
||||
pub fn dump(&self, idx: c_int, mode: DumpMode) -> Result<BString> {
|
||||
let func = self.slot(idx);
|
||||
assert!(
|
||||
func.type_of() == Type::Function,
|
||||
@@ -881,6 +929,24 @@ impl Stack {
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluates the given chunk on the stack synchronously with `narg` values at the top of the
|
||||
/// stack as arguments.
|
||||
///
|
||||
/// Equivalent to calling [`load`](Self::load) on the chunk and then [`call`](Self::call) on the
|
||||
/// loaded function.
|
||||
///
|
||||
/// # Panic
|
||||
///
|
||||
/// Panics if there are not enough values on the stack or thread status is invalid.
|
||||
pub fn eval(&mut self, chunk: &Chunk, narg: c_int, nret: c_int) -> Result<c_int> {
|
||||
assert!(0 <= narg && (0 <= nret || nret == LUA_MULTRET));
|
||||
let base = self.size() - narg;
|
||||
assert!(base >= 0, "expected {narg} values: {self:?}");
|
||||
self.load(chunk)?;
|
||||
self.pop_insert(base + 1);
|
||||
self.call(narg, nret)
|
||||
}
|
||||
|
||||
/// Calls a function on the stack synchronously with `narg` values at the top of the stack as
|
||||
/// arguments.
|
||||
///
|
||||
@@ -888,8 +954,8 @@ impl Stack {
|
||||
/// the index `top - narg` (i.e. the function is pushed first and then `narg` values as
|
||||
/// arguments). All arguments and the function are popped from the stack and then any return
|
||||
/// values are pushed. If `nret` is not [`LUA_MULTRET`], then the number of return values pushed
|
||||
/// will be exactly `nret`, filling with nils if necessary. Finally, the number of return values
|
||||
/// pushed to the stack is returned.
|
||||
/// will be exactly `nret`, filling with nils if necessary. Finally, the number of returned
|
||||
/// values pushed to the stack is returned.
|
||||
///
|
||||
/// The current thread status must not be suspended or dead.
|
||||
///
|
||||
@@ -899,7 +965,7 @@ impl Stack {
|
||||
///
|
||||
/// Panics if there are not enough values on the stack, the function to call is not on the
|
||||
/// stack, or thread status is invalid.
|
||||
pub fn call(&mut self, narg: c_int, nret: c_int) -> Result<c_int, Error> {
|
||||
pub fn call(&mut self, narg: c_int, nret: c_int) -> Result<c_int> {
|
||||
assert!(0 <= narg && (0 <= nret || nret == LUA_MULTRET));
|
||||
|
||||
let top = self.size();
|
||||
@@ -945,8 +1011,8 @@ impl Stack {
|
||||
/// the index `top - narg` (i.e. the function is pushed first and then `narg` values as
|
||||
/// arguments). All arguments and the function are popped from the stack and then any return
|
||||
/// values are pushed. If `nret` is not [`LUA_MULTRET`], then the number of return values pushed
|
||||
/// will be exactly `nret`, filling with nils if necessary. Finally, the number of return values
|
||||
/// pushed to the stack is returned.
|
||||
/// will be exactly `nret`, filling with nils if necessary. Finally, the number of returned
|
||||
/// values pushed to the stack is returned.
|
||||
///
|
||||
/// If the thread yields a Rust [`Future`] value, then it will be polled to completion before
|
||||
/// the thread is resumed with the output of the [`Future`] as the argument. If the thread
|
||||
@@ -961,7 +1027,7 @@ impl Stack {
|
||||
///
|
||||
/// Panics if there are not enough values on the stack, the function to call is not on the
|
||||
/// stack, or thread status is invalid.
|
||||
pub async fn call_async(&mut self, mut narg: c_int, nret: c_int) -> Result<c_int, Error> {
|
||||
pub async fn call_async(&mut self, mut narg: c_int, nret: c_int) -> Result<c_int> {
|
||||
assert!(0 <= narg && (0 <= nret || nret == LUA_MULTRET));
|
||||
|
||||
let top = self.size();
|
||||
@@ -1015,7 +1081,7 @@ impl Stack {
|
||||
/// pushed first and then `narg` values as arguments). If the current thread status is
|
||||
/// suspended, then there must be `narg` values at the top of the stack. All arguments and the
|
||||
/// function are popped from the stack and then any yielded values are pushed. Finally, the new
|
||||
/// status of the thread is returned.
|
||||
/// status of the thread indicating whether the thread had completed or suspended is returned.
|
||||
///
|
||||
/// The current thread status must not be dead.
|
||||
///
|
||||
@@ -1025,7 +1091,7 @@ impl Stack {
|
||||
///
|
||||
/// Panics if there are not enough values on the stack, the function to call is not on the
|
||||
/// stack, or thread status is invalid.
|
||||
pub fn resume(&mut self, narg: c_int) -> Result<ResumeStatus, Error> {
|
||||
pub fn resume(&mut self, narg: c_int) -> Result<ResumeStatus> {
|
||||
assert!(0 <= narg);
|
||||
let status = self.status();
|
||||
let need = match status {
|
||||
@@ -1237,7 +1303,7 @@ impl<'s> Slot<'s> {
|
||||
}
|
||||
|
||||
/// Parses the value in this slot as a `T`.
|
||||
pub fn parse<T: Parse<'s>>(&self) -> Result<T, Error> {
|
||||
pub fn parse<T: Parse<'s>>(&self) -> Result<T> {
|
||||
T::parse(self)
|
||||
}
|
||||
|
||||
@@ -1264,6 +1330,9 @@ impl<'s> Slot<'s> {
|
||||
|
||||
/// Parses the value in this slot as a [`lua_Number`].
|
||||
///
|
||||
/// If the value is not a `number` or a `string` that can be parsed as a number, then this
|
||||
/// returns [`None`].
|
||||
///
|
||||
/// Equivalent to [`lua_tonumberx`].
|
||||
pub fn number(&self) -> Option<lua_Number> {
|
||||
self.parse().ok()
|
||||
@@ -1271,6 +1340,9 @@ impl<'s> Slot<'s> {
|
||||
|
||||
/// Parses the value in this slot as a [`lua_Integer`].
|
||||
///
|
||||
/// If the value is not a `number` or a `string` that can be parsed as an integer, then this
|
||||
/// returns [`None`].
|
||||
///
|
||||
/// Equivalent to [`lua_tointegerx`].
|
||||
pub fn integer(&self) -> Option<lua_Integer> {
|
||||
self.parse().ok()
|
||||
@@ -1278,6 +1350,11 @@ impl<'s> Slot<'s> {
|
||||
|
||||
/// Parses the value in this slot as a binary string.
|
||||
///
|
||||
/// If the value is a `number`, then it is converted in-place into a `string` representation of
|
||||
/// the value first.
|
||||
///
|
||||
/// If the value is not a `string`, then this returns [`None`].
|
||||
///
|
||||
/// Equivalent to [`lua_tolstring`].
|
||||
pub fn string(&self) -> Option<&'s BStr> {
|
||||
self.parse().ok()
|
||||
@@ -1285,12 +1362,19 @@ impl<'s> Slot<'s> {
|
||||
|
||||
/// Parses the value in this slot as a UTF-8 string.
|
||||
///
|
||||
/// If the value is a `number`, then it is converted in-place into a `string` representation
|
||||
/// first.
|
||||
///
|
||||
/// Equivalent to [`lua_tolstring`].
|
||||
pub fn string_utf8(&self) -> Option<&'s str> {
|
||||
self.parse().ok()
|
||||
}
|
||||
|
||||
/// Parses the value in this slot as a [`lua_CFunction`].
|
||||
///
|
||||
/// If the value is not a C function, then this returns [`None`].
|
||||
///
|
||||
/// Equivalent to [`lua_tocfunction`].
|
||||
pub fn function_raw(&self) -> lua_CFunction {
|
||||
unsafe { lua_tocfunction(self.stack.as_ptr(), self.idx) }
|
||||
}
|
||||
@@ -1299,6 +1383,8 @@ impl<'s> Slot<'s> {
|
||||
///
|
||||
/// If the value is a `cdata`, then the returned pointer is the address of the base of the cdata
|
||||
/// payload. Otherwise this returns a null pointer.
|
||||
///
|
||||
/// Equivalent to [`lua_topointer`].
|
||||
pub fn cdata<T>(&self) -> *const T {
|
||||
(self.type_of() == Type::Cdata)
|
||||
.then(|| self.pointer().cast())
|
||||
@@ -1306,9 +1392,28 @@ impl<'s> Slot<'s> {
|
||||
}
|
||||
|
||||
/// Parses the value in this slot as a generic pointer.
|
||||
///
|
||||
/// If the value is not a GC-managed object that can be represented by a pointer, then this
|
||||
/// returns a null pointer.
|
||||
///
|
||||
/// Equivalent to [`lua_topointer`].
|
||||
pub fn pointer(&self) -> *const c_void {
|
||||
unsafe { lua_topointer(self.stack.as_ptr(), self.idx).cast() }
|
||||
}
|
||||
|
||||
/// Returns the length of the value in this slot.
|
||||
///
|
||||
/// For strings, this is the byte-length of the contents of the string. For tables, this is the
|
||||
/// length of the table defined by the Lua `#` operator. For userdata, this is the size of its
|
||||
/// payload in bytes. For numbers, this is equivalent to converting the value in-place into a
|
||||
/// `string` representation before calculating its length. Otherwise, this returns 0.
|
||||
///
|
||||
/// This function does not invoke the `__len` metamethod.
|
||||
///
|
||||
/// Equivalent to [`lua_objlen`].
|
||||
pub fn length(&self) -> usize {
|
||||
unsafe { lua_objlen(self.stack.as_ptr(), self.idx) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Pushes a value onto a [`Stack`].
|
||||
@@ -1509,11 +1614,11 @@ impl Push for CurrentThread {
|
||||
/// [`Slot::parse`].
|
||||
pub trait Parse<'s>: Sized {
|
||||
/// Parses the value in the given slot.
|
||||
fn parse(slot: &Slot<'s>) -> Result<Self, Error>;
|
||||
fn parse(slot: &Slot<'s>) -> Result<Self>;
|
||||
}
|
||||
|
||||
impl Parse<'_> for () {
|
||||
fn parse(slot: &Slot) -> Result<Self, Error> {
|
||||
fn parse(slot: &Slot) -> Result<Self> {
|
||||
match slot.type_of() {
|
||||
Type::Nil => Ok(()),
|
||||
ty => Err(Error::InvalidType("nil", ty.name())),
|
||||
@@ -1522,7 +1627,7 @@ impl Parse<'_> for () {
|
||||
}
|
||||
|
||||
impl Parse<'_> for bool {
|
||||
fn parse(slot: &Slot) -> Result<Self, Error> {
|
||||
fn parse(slot: &Slot) -> Result<Self> {
|
||||
Ok(unsafe { lua_toboolean(slot.stack.as_ptr(), slot.index()) != 0 })
|
||||
}
|
||||
}
|
||||
@@ -1530,7 +1635,7 @@ impl Parse<'_> for bool {
|
||||
macro_rules! impl_parse_ptr {
|
||||
($type:ty) => {
|
||||
impl<T> Parse<'_> for $type {
|
||||
fn parse(slot: &Slot) -> Result<Self, Error> {
|
||||
fn parse(slot: &Slot) -> Result<Self> {
|
||||
let ptr = unsafe { lua_touserdata(slot.stack.as_ptr(), slot.idx) };
|
||||
if !ptr.is_null() {
|
||||
Ok(ptr as $type)
|
||||
@@ -1548,7 +1653,7 @@ impl_parse_ptr!(*const T);
|
||||
macro_rules! impl_parse_num {
|
||||
($type:ty) => {
|
||||
impl Parse<'_> for $type {
|
||||
fn parse(slot: &Slot) -> Result<Self, Error> {
|
||||
fn parse(slot: &Slot) -> Result<Self> {
|
||||
let mut isnum = 0;
|
||||
let n = unsafe { lua_tonumberx(slot.stack.as_ptr(), slot.idx, &raw mut isnum) };
|
||||
if isnum != 0 {
|
||||
@@ -1567,7 +1672,7 @@ impl_parse_num!(f64);
|
||||
macro_rules! impl_parse_int {
|
||||
($type:ty) => {
|
||||
impl Parse<'_> for $type {
|
||||
fn parse(slot: &Slot) -> Result<Self, Error> {
|
||||
fn parse(slot: &Slot) -> Result<Self> {
|
||||
let mut isnum = 0;
|
||||
let n = unsafe { lua_tointegerx(slot.stack.as_ptr(), slot.idx, &raw mut isnum) };
|
||||
if isnum != 0 {
|
||||
@@ -1594,7 +1699,7 @@ impl_parse_int!(isize);
|
||||
macro_rules! impl_parse_str {
|
||||
($type:ty) => {
|
||||
impl<'s> Parse<'s> for $type {
|
||||
fn parse(slot: &Slot<'s>) -> Result<Self, Error> {
|
||||
fn parse(slot: &Slot<'s>) -> Result<Self> {
|
||||
let mut len = 0;
|
||||
let ptr = unsafe { lua_tolstring(slot.stack.as_ptr(), slot.idx, &mut len) };
|
||||
if !ptr.is_null() {
|
||||
@@ -1610,7 +1715,7 @@ macro_rules! impl_parse_str {
|
||||
macro_rules! impl_parse_str_utf8 {
|
||||
($type:ty) => {
|
||||
impl<'s> Parse<'s> for $type {
|
||||
fn parse(slot: &Slot<'s>) -> Result<Self, Error> {
|
||||
fn parse(slot: &Slot<'s>) -> Result<Self> {
|
||||
Ok(std::str::from_utf8(Parse::parse(slot)?)?.into())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user