Compare commits

...

13 Commits

Author SHA1 Message Date
Alex Orlenko d8b9c46d28 0.2.2 release 2020-02-09 22:21:34 +00:00
Alex Orlenko 9bd6d6fa92 Fix compile fail tests 2020-02-09 22:09:42 +00:00
Alex Orlenko ea6e6e00b2 Update README.md 2020-02-09 21:48:50 +00:00
Alex Orlenko 03ed7f6f99 Fix compile test 2020-01-27 21:57:12 +00:00
Alex Orlenko d5bc9c8c24 Combine lua-vendored and luajit-vendored features into "vendored" 2020-01-27 21:21:50 +00:00
Alex Orlenko 5cd0b645c4 Merge branch 'github_actions' 2020-01-25 23:07:31 +00:00
Alex Orlenko bf36eb72f8 Move from circele-ci to github actions 2020-01-25 22:35:47 +00:00
Alex Orlenko 1f7f8f62ab Merge branch 'lua_vendored' 2020-01-25 20:49:00 +00:00
Alex Orlenko e4dc773aa3 Remove __ipairs metamethod deprecated in lua 5.3 and not available by default 2020-01-25 20:47:36 +00:00
Alex Orlenko 07fc4642ae Support vendored versions of Lua and LuaJIT 2020-01-25 20:47:31 +00:00
Alex Orlenko 27121c779d Add raw_insert() and raw_remove() for tables (represented as lists) 2020-01-07 20:53:47 +00:00
Alex Orlenko 5eec0ef56b Implement PartialEq trait for Value (and subtypes)
Add equals() method to compare values optionally invoking __eq.
2020-01-07 00:03:03 +00:00
Alex Orlenko 831161bfda Fix CI badge link 2020-01-06 23:55:56 +00:00
28 changed files with 825 additions and 421 deletions
-49
View File
@@ -1,49 +0,0 @@
version: 2
jobs:
build:
docker:
- image: circleci/rust:latest
steps:
- checkout
- run:
name: Version information
command: rustc --version; cargo --version; rustup --version
- run:
name: Calculate dependencies
command: cargo generate-lockfile
- restore_cache:
keys:
- cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }}
- run:
name: Check Formatting
command: |
rustup component add rustfmt
rustfmt --version
cargo fmt --all -- --check --color=auto
- run:
name: Install Lua
command: |
sudo apt-get update
sudo apt-get -y --no-install-recommends install liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
- run:
name: Build all targets
command: cargo build --all --all-targets
- run:
name: Run all tests / Lua 5.3
command: cargo test --all --no-default-features --features lua53
- run:
name: Run all tests / Lua 5.2
command: cargo test --all --no-default-features --features lua52
- run:
name: Run all tests / Lua 5.1
command: cargo test --all --no-default-features --features lua51
- run:
name: Run all tests / LuaJIT
command: cargo test --all --no-default-features --features luajit
- save_cache:
paths:
- /usr/local/cargo/registry
- target/debug/.fingerprint
- target/debug/build
- target/debug/deps
key: cargo-cache-{{ arch }}-{{ checksum "Cargo.lock" }}
+147
View File
@@ -0,0 +1,147 @@
name: CI
on: [push, pull_request]
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
thing:
- stable
- macos-x86_64
- x86_64-msvc
include:
- thing: stable
target: x86_64-unknown-linux-gnu
rust: stable
os: ubuntu-latest
- thing: macos-x86_64
target: x86_64-apple-darwin
rust: stable
os: macos-latest
- thing: x86_64-msvc
target: x86_64-pc-windows-msvc
rust: stable-x86_64-msvc
os: windows-latest
steps:
- uses: actions/checkout@v1
- name: Install Rust (rustup)
if: matrix.os != 'macos-latest'
run: rustup update ${{ matrix.rust }} --no-self-update && rustup default ${{ matrix.rust }}
shell: bash
- name: Install Lua (ubuntu)
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends liblua5.3-dev liblua5.2-dev liblua5.1-0-dev libluajit-5.1-dev
- name: Install Rust (macos)
if: matrix.os == 'macos-latest'
run: |
curl https://sh.rustup.rs | sh -s -- -y
echo ::add-path::$HOME/.cargo/bin
shell: bash
- name: Install GCC (aarch64-linux)
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends gcc-aarch64-linux-gnu libc6-dev-arm64-cross
if: matrix.thing == 'aarch64-linux'
shell: bash
- run: rustup target add ${{ matrix.target }}
- name: Build (Lua 5.3/5.2/5.1 and LuaJIT vendored)
run: |
for FEATURE in lua53 lua52 lua51 luajit; do
echo "Building $FEATURE"
cargo build --target ${{ matrix.target }} --release --no-default-features --features "$FEATURE vendored"
done
shell: bash
- name: Build (Lua 5.3/5.2/5.1 and LuaJIT via pkg-config)
if: matrix.os == 'ubuntu-latest'
run: |
for FEATURE in lua53 lua52 lua51 luajit; do
echo "Building $FEATURE"
cargo build --target ${{ matrix.target }} --release --no-default-features --features $FEATURE
done
shell: bash
test_linux:
name: Test on Linux
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v1
- name: Install Rust
run: rustup update stable --no-self-update && rustup default stable
shell: bash
- name: Run tests (Lua 5.3 vendored)
run: cargo test --release --no-default-features --features "lua53 vendored"
shell: bash
- name: Run tests (Lua 5.2 vendored)
run: cargo test --release --no-default-features --features "lua52 vendored"
shell: bash
- name: Run tests (Lua 5.1 vendored)
run: cargo test --release --no-default-features --features "lua51 vendored"
shell: bash
- name: Run tests (LuaJIT vendored)
run: cargo test --release --no-default-features --features "luajit vendored"
shell: bash
- name: Run compile test (Lua 5.3)
run: |
sudo apt-get update -y
sudo apt-get install -y --no-install-recommends liblua5.3-dev
cargo test --release --no-default-features --features "lua53 vendored" -- --ignored
test_macos:
name: Test on MacOS
runs-on: macos-latest
needs: build
steps:
- uses: actions/checkout@v1
- name: Install Rust
run: |
curl https://sh.rustup.rs | sh -s -- -y
echo ::add-path::$HOME/.cargo/bin
shell: bash
- name: Run tests (Lua 5.3 vendored)
run: cargo test --release --no-default-features --features "lua53 vendored"
shell: bash
- name: Run tests (Lua 5.2 vendored)
run: cargo test --release --no-default-features --features "lua52 vendored"
shell: bash
- name: Run tests (Lua 5.1 vendored)
run: cargo test --release --no-default-features --features "lua51 vendored"
shell: bash
- name: Run tests (LuaJIT vendored)
run: cargo test --release --no-default-features --features "luajit vendored"
shell: bash
test_windows:
name: Test on Windows
runs-on: windows-latest
needs: build
steps:
- uses: actions/checkout@v1
- name: Install Rust
run: rustup update stable --no-self-update && rustup default stable
shell: bash
- name: Run tests (Lua 5.3 vendored)
run: cargo test --release --no-default-features --features "lua53 vendored"
shell: bash
- name: Run tests (Lua 5.2 vendored)
run: cargo test --release --no-default-features --features "lua52 vendored"
shell: bash
- name: Run tests (Lua 5.1 vendored)
run: cargo test --release --no-default-features --features "lua51 vendored"
shell: bash
- name: Run tests (LuaJIT vendored)
run: cargo test --release --no-default-features --features "luajit vendored"
shell: bash
rustfmt:
name: Rustfmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Install Rust
run: rustup update stable && rustup default stable && rustup component add rustfmt
- run: cargo fmt -- --check
+7 -2
View File
@@ -1,6 +1,6 @@
[package]
name = "mlua"
version = "0.2.1"
version = "0.2.2"
authors = ["Aleksandr Orlenko <zxteam@pm.me>", "kyren <catherine@chucklefish.org>"]
edition = "2018"
repository = "https://github.com/khvzak/mlua"
@@ -9,13 +9,15 @@ readme = "README.md"
keywords = ["lua", "luajit"]
categories = ["api-bindings"]
license = "MIT"
links = "lua"
build = "build/main.rs"
description = """
High level bindings to Lua 5.1/5.2/5.3 (including LuaJIT)
with support of writing native lua modules in Rust.
"""
[badges]
travis-ci = { repository = "khvzak/mlua", branch = "master" }
# github-actions = { repository = "khvzak/mlua", workflow = "CI" }
[workspace]
members = [
@@ -28,6 +30,7 @@ lua53 = []
lua52 = []
lua51 = []
luajit = []
vendored = ["lua-src", "luajit-src"]
[dependencies]
num-traits = { version = "0.2.6" }
@@ -36,6 +39,8 @@ bstr = { version = "0.2", features = ["std"], default_features = false }
[build-dependencies]
cc = { version = "1.0" }
pkg-config = { version = "0.3.11" }
lua-src = { version = "535.0.1", optional = true }
luajit-src = { version = "210.0.0", optional = true }
[dev-dependencies]
rustyline = "5.0"
+24 -6
View File
@@ -1,8 +1,8 @@
# mlua
[![Build Status]][circleci] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
[![Build Status]][github-actions] [![Latest Version]][crates.io] [![API Documentation]][docs.rs]
[Build Status]: https://img.shields.io/circleci/project/github/khvzak/mlua.svg
[circleci]: https://circleci.com/gh/khvzak/mlua
[Build Status]: https://github.com/khvzak/mlua/workflows/CI/badge.svg
[github-actions]: https://github.com/khvzak/mlua/actions
[Latest Version]: https://img.shields.io/crates/v/mlua.svg
[crates.io]: https://crates.io/crates/mlua
[API Documentation]: https://docs.rs/mlua/badge.svg
@@ -16,9 +16,27 @@ The `mlua` goal is to be an easy to use, practical and flexible API between Rust
*__not__* always 100% safe due to the Lua VM nature. Also, `mlua` provides a way to write native lua
modules in Rust.
### Usage
## Usage
#### standalone mode
### Choosing Lua version
The following features could be used to choose Lua version: `lua53` (default), `lua52`, `lua51` and `luajit`.
By default mlua uses `pkg-config` tool to find lua includes and lib.
In most cases it works as desired, although sometimes could be more preferable to use a custom lua library.
To achieve this, mlua supports `LUA_INC`, `LUA_LIB`, `LUA_LIB_NAME` and `LUA_LINK` environment variables.
`LUA_LINK` is optional and may be `dylib` (a dynamic library) or `static` (a static library, `.a` archive).
An example how to use them:
``` sh
my_project $ LUA_INC=$HOME/tmp/lua-5.2.4/src LUA_LIB=$HOME/tmp/lua-5.2.4/src LUA_LIB_NAME=lua LUA_LINK=static cargo build
```
`mlua` also supports vendored lua/luajit using the auxilary crates [lua-src](https://crates.io/crates/lua-src) and
[luajit-src](https://crates.io/crates/luajit-src).
Just enable the `vendored` feature and cargo will automatically build and link specified lua/luajit version. This is the easiest way to get started with mlua.
### Standalone mode
Add to `Cargo.toml` :
``` toml
@@ -46,7 +64,7 @@ fn main() -> LuaResult<()> {
}
```
#### module mode
### Module mode
Add to `Cargo.toml` :
-189
View File
@@ -1,189 +0,0 @@
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, Error, ErrorKind, Result};
use std::ops::Bound;
use std::path::{Path, PathBuf};
use std::process::Command;
trait CommandExt {
fn execute(&mut self) -> Result<()>;
}
impl CommandExt for Command {
/// Execute the command and return an error if it exited with a failure status.
fn execute(&mut self) -> Result<()> {
self.status()
.and_then(|status| {
if status.success() {
Ok(())
} else {
Err(Error::new(ErrorKind::Other, "non-zero exit code"))
}
})
.map_err(|_| {
Error::new(
ErrorKind::Other,
format!("The command {:?} did not run successfully.", self),
)
})
}
}
fn use_custom_lua<S: AsRef<str>>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result<String> {
let mut version_found = String::new();
// Find LUA_VERSION_NUM
let mut lua_h_path = PathBuf::from(include_dir.as_ref());
lua_h_path.push("lua.h");
let f = File::open(lua_h_path)?;
let reader = BufReader::new(f);
for line in reader.lines() {
let line = line?;
let parts = line.split_whitespace().collect::<Vec<_>>();
if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" {
version_found = parts[2].to_string();
}
}
let mut static_link = "";
if env::var("LUA_LINK").unwrap_or(String::new()) == "static" {
static_link = "static=";
}
println!("cargo:rustc-link-search=native={}", lib_dir.as_ref());
println!("cargo:rustc-link-lib={}{}", static_link, lua_lib.as_ref());
Ok(version_found)
}
fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_paths: &[P]) {
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
// Ensure the presence of glue.rs
// if build_dir.join("glue.rs").exists() {
// return;
// }
let mut config = cc::Build::new();
for path in include_paths {
config.include(path);
}
// Compile and run glue.c
let glue = build_dir.join("glue");
config
.get_compiler()
.to_command()
.arg("src/ffi/glue/glue.c")
.arg("-o")
.arg(&glue)
.execute()
.unwrap();
Command::new(glue)
.arg(build_dir.join("glue.rs"))
.execute()
.unwrap();
}
fn main() {
let include_dir = env::var("LUA_INC").unwrap_or(String::new());
let lib_dir = env::var("LUA_LIB").unwrap_or(String::new());
let lua_lib = env::var("LUA_LIB_NAME").unwrap_or(String::new());
println!("cargo:rerun-if-env-changed=LUA_INC");
println!("cargo:rerun-if-env-changed=LUA_LIB");
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
println!("cargo:rerun-if-env-changed=LUA_LINK");
println!("cargo:rerun-if-changed=src/ffi/glue/glue.c");
if include_dir != "" && lib_dir != "" && lua_lib != "" {
let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
build_glue(&[include_dir]);
return;
}
// Find lua via pkg-config
#[cfg(not(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)))]
panic!("You must enable one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(
feature = "lua53",
any(feature = "lua52", feature = "lua51", feature = "luajit")
))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua51", feature = "luajit"))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(feature = "lua53")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.3");
}
match lua {
Ok(lua) => build_glue(&lua.include_paths),
Err(err) => panic!(err),
};
}
#[cfg(feature = "lua52")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.2");
}
match lua {
Ok(lua) => build_glue(&lua.include_paths),
Err(_) => panic!("Lua 5.2 not found"),
};
}
#[cfg(feature = "lua51")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.1");
}
match lua {
Ok(lua) => build_glue(&lua.include_paths),
Err(err) => panic!(err),
};
}
#[cfg(feature = "luajit")]
{
let lua = pkg_config::Config::new()
.range_version((Bound::Included("2.0.5"), Bound::Unbounded))
.probe("luajit");
match lua {
Ok(lua) => build_glue(&lua.include_paths),
Err(err) => panic!(err),
};
}
}
+106
View File
@@ -0,0 +1,106 @@
use std::env;
use std::ffi::OsString;
use std::fs::File;
use std::io::{BufRead, BufReader, Result};
use std::ops::Bound;
use std::path::{Path, PathBuf};
pub fn probe_lua() -> PathBuf {
let include_dir = env::var_os("LUA_INC").unwrap_or(OsString::new());
let lib_dir = env::var_os("LUA_LIB").unwrap_or(OsString::new());
let lua_lib = env::var_os("LUA_LIB_NAME").unwrap_or(OsString::new());
println!("cargo:rerun-if-env-changed=LUA_INC");
println!("cargo:rerun-if-env-changed=LUA_LIB");
println!("cargo:rerun-if-env-changed=LUA_LIB_NAME");
println!("cargo:rerun-if-env-changed=LUA_LINK");
if include_dir != "" && lib_dir != "" && lua_lib != "" {
let _version = use_custom_lua(&include_dir, &lib_dir, &lua_lib).unwrap();
return PathBuf::from(include_dir);
}
// Find using via pkg-config
#[cfg(feature = "lua53")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.3"), Bound::Excluded("5.4")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.3");
}
return lua.unwrap().include_paths[0].clone();
}
#[cfg(feature = "lua52")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.2"), Bound::Excluded("5.3")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.2");
}
return lua.unwrap().include_paths[0].clone();
}
#[cfg(feature = "lua51")]
{
let mut lua = pkg_config::Config::new()
.range_version((Bound::Included("5.1"), Bound::Excluded("5.2")))
.probe("lua");
if lua.is_err() {
lua = pkg_config::Config::new().probe("lua5.1");
}
return lua.unwrap().include_paths[0].clone();
}
#[cfg(feature = "luajit")]
{
let lua = pkg_config::Config::new()
.range_version((Bound::Included("2.1.0"), Bound::Unbounded))
.probe("luajit");
return lua.unwrap().include_paths[0].clone();
}
}
fn use_custom_lua<S: AsRef<Path>>(include_dir: &S, lib_dir: &S, lua_lib: &S) -> Result<String> {
let mut version_found = String::new();
// Find LUA_VERSION_NUM
let mut lua_h_path = include_dir.as_ref().to_owned();
lua_h_path.push("lua.h");
let f = File::open(lua_h_path)?;
let reader = BufReader::new(f);
for line in reader.lines() {
let line = line?;
let parts = line.split_whitespace().collect::<Vec<_>>();
if parts.len() == 3 && parts[1] == "LUA_VERSION_NUM" {
version_found = parts[2].to_string();
}
}
let mut link_lib = String::new();
if env::var("LUA_LINK").unwrap_or(String::new()) == "static" {
link_lib = "static=".to_string();
}
println!(
"cargo:rustc-link-search=native={}",
lib_dir.as_ref().display()
);
println!(
"cargo:rustc-link-lib={}{}",
link_lib,
lua_lib.as_ref().display()
);
Ok(version_found)
}
+20
View File
@@ -0,0 +1,20 @@
use std::path::PathBuf;
#[cfg(any(feature = "lua53", feature = "lua52", feature = "lua51"))]
use lua_src;
#[cfg(feature = "luajit")]
use luajit_src;
pub fn probe_lua() -> PathBuf {
#[cfg(feature = "lua53")]
let artifacts = lua_src::Build::new().build(lua_src::Lua53);
#[cfg(feature = "lua52")]
let artifacts = lua_src::Build::new().build(lua_src::Lua52);
#[cfg(feature = "lua51")]
let artifacts = lua_src::Build::new().build(lua_src::Lua51);
#[cfg(feature = "luajit")]
let artifacts = luajit_src::Build::new().build();
artifacts.print_cargo_metadata();
artifacts.include_dir().to_owned()
}
+86
View File
@@ -0,0 +1,86 @@
#![allow(unreachable_code)]
use std::env;
use std::io::{Error, ErrorKind, Result};
use std::path::{Path, PathBuf};
use std::process::Command;
#[cfg_attr(feature = "vendored", path = "find_vendored.rs")]
#[cfg_attr(not(feature = "vendored"), path = "find_normal.rs")]
mod find;
trait CommandExt {
fn execute(&mut self) -> Result<()>;
}
impl CommandExt for Command {
/// Execute the command and return an error if it exited with a failure status.
fn execute(&mut self) -> Result<()> {
self.status()
.and_then(|status| {
if status.success() {
Ok(())
} else {
Err(Error::new(ErrorKind::Other, "non-zero exit code"))
}
})
.map_err(|_| {
Error::new(
ErrorKind::Other,
format!("The command {:?} did not run successfully.", self),
)
})
}
}
fn build_glue<P: AsRef<Path> + std::fmt::Debug>(include_path: &P) {
let build_dir = PathBuf::from(env::var_os("OUT_DIR").unwrap());
let mut config = cc::Build::new();
config.include(include_path);
// Compile and run glue.c
let glue = build_dir.join("glue");
config
.get_compiler()
.to_command()
.arg("src/ffi/glue/glue.c")
.arg("-o")
.arg(&glue)
.execute()
.unwrap();
Command::new(glue)
.arg(build_dir.join("glue.rs"))
.execute()
.unwrap();
}
fn main() {
#[cfg(not(any(
feature = "lua53",
feature = "lua52",
feature = "lua51",
feature = "luajit"
)))]
panic!("You must enable one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(
feature = "lua53",
any(feature = "lua52", feature = "lua51", feature = "luajit")
))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua52", any(feature = "lua51", feature = "luajit")))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua51", feature = "luajit"))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
#[cfg(all(feature = "lua51", feature = "luajit"))]
panic!("You can enable only one of the features: lua53, lua52, lua51, luajit");
let include_dir = find::probe_lua();
build_glue(&include_dir);
}
+6
View File
@@ -160,3 +160,9 @@ impl<'lua> Function<'lua> {
}
}
}
impl<'lua> PartialEq for Function<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
+132 -6
View File
@@ -1,7 +1,7 @@
use std::marker::PhantomData;
use std::os::raw::c_int;
use crate::error::Result;
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::types::{Integer, LuaRef};
@@ -166,11 +166,60 @@ impl<'lua> Table<'lua> {
self.get::<_, Function>(key)?.call(args)
}
/// Removes a key from the table, returning the value at the key
/// if the key was previously in the table.
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
self.raw_set(key, Nil)?;
Ok(())
/// Compares two tables for equality.
///
/// Tables are compared by reference first.
/// If they are not primitively equals, then mlua will try to invoke the `__eq` metamethod.
/// mlua will check `self` first for the metamethod, then `other` if not found.
///
/// # Examples
///
/// Compare two tables using `__eq` metamethod:
///
/// ```
/// # use mlua::{Lua, Result, Table};
/// # fn main() -> Result<()> {
/// # let lua = Lua::new();
/// let table1 = lua.create_table()?;
/// table1.set(1, "value")?;
///
/// let table2 = lua.create_table()?;
/// table2.set(2, "value")?;
///
/// let always_equals_mt = lua.create_table()?;
/// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
/// table2.set_metatable(Some(always_equals_mt));
///
/// assert!(table1.equals(&table1.clone())?);
/// assert!(table1.equals(&table2)?);
/// # Ok(())
/// # }
/// ```
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
let other = other.as_ref();
if self == other {
return Ok(true);
}
// Compare using __eq metamethod if exists
// First, check the self for the metamethod.
// If self does not define it, then check the other table.
if let Some(mt) = self.get_metatable() {
if mt.contains_key("__eq")? {
return mt
.get::<_, Function>("__eq")?
.call((self.clone(), other.clone()));
}
}
if let Some(mt) = other.get_metatable() {
if mt.contains_key("__eq")? {
return mt
.get::<_, Function>("__eq")?
.call((self.clone(), other.clone()));
}
}
Ok(false)
}
/// Sets a key-value pair without invoking metamethods.
@@ -213,6 +262,70 @@ impl<'lua> Table<'lua> {
V::from_lua(value, lua)
}
/// Inserts element value at position idx to the table, shifting up the elements from table[idx].
/// The worst case complexity is O(n), where n is the table length.
pub fn raw_insert<V: ToLua<'lua>>(&self, idx: Integer, value: V) -> Result<()> {
let lua = self.0.lua;
let size = self.raw_len();
if idx < 1 || idx > size + 1 {
return Err(Error::RuntimeError("index out of bounds".to_string()));
}
let value = value.to_lua(lua)?;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 6);
lua.push_ref(&self.0);
lua.push_value(value)?;
protect_lua_closure(lua.state, 2, 0, |state| {
for i in (idx..size + 1).rev() {
// table[i+1] = table[i]
ffi::lua_rawgeti(state, -2, i);
ffi::lua_rawseti(state, -3, i + 1);
}
ffi::lua_rawseti(state, -2, idx);
})
}
}
/// Removes a key from the table.
///
/// If `key` is an integer, mlua shifts down the elements from table[key+1],
/// and erases element table[key]. The complexity is O(n) in worst case,
/// where n is the table length.
///
/// For othey key types this is equivalent to setting table[key] = nil.
pub fn raw_remove<K: ToLua<'lua>>(&self, key: K) -> Result<()> {
let lua = self.0.lua;
let key = key.to_lua(lua)?;
match key {
Value::Integer(idx) => {
let size = self.raw_len();
if idx < 1 || idx > size {
return Err(Error::RuntimeError("index out of bounds".to_string()));
}
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 6);
lua.push_ref(&self.0);
protect_lua_closure(lua.state, 1, 0, |state| {
for i in idx..size {
ffi::lua_rawgeti(state, -1, i + 1);
ffi::lua_rawseti(state, -2, i);
}
ffi::lua_pushnil(state);
ffi::lua_rawseti(state, -2, size);
})
}
}
_ => self.raw_set(key, Nil),
}
}
/// Returns the result of the Lua `#` operator.
///
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
@@ -368,6 +481,19 @@ impl<'lua> Table<'lua> {
}
}
impl<'lua> PartialEq for Table<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<'lua> AsRef<Table<'lua>> for Table<'lua> {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
/// An iterator over the pairs of a Lua table.
///
/// This struct is created by the [`Table::pairs`] method.
+6
View File
@@ -143,3 +143,9 @@ impl<'lua> Thread<'lua> {
}
}
}
impl<'lua> PartialEq for Thread<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
+14
View File
@@ -5,6 +5,7 @@ use std::{fmt, mem, ptr};
use crate::error::Result;
use crate::ffi;
use crate::lua::Lua;
use crate::util::{assert_stack, StackGuard};
use crate::value::MultiValue;
/// Type of Lua integer numbers.
@@ -92,3 +93,16 @@ impl<'lua> Drop for LuaRef<'lua> {
self.lua.drop_ref(self)
}
}
impl<'lua> PartialEq for LuaRef<'lua> {
fn eq(&self, other: &Self) -> bool {
let lua = self.lua;
unsafe {
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 2);
lua.push_ref(&self);
lua.push_ref(&other);
ffi::lua_rawequal(lua.state, -1, -2) == 1
}
}
}
+51 -7
View File
@@ -2,7 +2,9 @@ use std::cell::{Ref, RefCell, RefMut};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::table::Table;
use crate::types::LuaRef;
use crate::util::{assert_stack, get_userdata, StackGuard};
use crate::value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti};
@@ -75,11 +77,6 @@ pub enum MetaMethod {
///
/// This is not an operator, but it will be called by the built-in `pairs` function.
Pairs,
#[cfg(any(feature = "lua53", feature = "lua52"))]
/// The `__ipairs` metamethod.
///
/// This is not an operator, but it will be called by the built-in `ipairs` function.
IPairs,
}
impl MetaMethod {
@@ -117,8 +114,6 @@ impl MetaMethod {
MetaMethod::ToString => b"__tostring",
#[cfg(any(feature = "lua53", feature = "lua52"))]
MetaMethod::Pairs => b"__pairs",
#[cfg(any(feature = "lua53", feature = "lua52"))]
MetaMethod::IPairs => b"__ipairs",
}
}
}
@@ -398,6 +393,42 @@ impl<'lua> AnyUserData<'lua> {
V::from_lua(res, lua)
}
fn get_metatable(&self) -> Result<Table<'lua>> {
unsafe {
let lua = self.0.lua;
let _sg = StackGuard::new(lua.state);
assert_stack(lua.state, 3);
lua.push_ref(&self.0);
if ffi::lua_getmetatable(lua.state, -1) == 0 {
return Err(Error::UserDataTypeMismatch);
}
Ok(Table(lua.pop_ref()))
}
}
pub(crate) fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
let other = other.as_ref();
if self == other {
return Ok(true);
}
let mt = self.get_metatable()?;
if mt != other.get_metatable()? {
return Ok(false);
}
if mt.contains_key("__eq")? {
return mt
.get::<_, Function>("__eq")?
.call((self.clone(), other.clone()));
}
Ok(false)
}
fn inspect<'a, T, R, F>(&'a self, func: F) -> Result<R>
where
T: 'static + UserData,
@@ -428,3 +459,16 @@ impl<'lua> AnyUserData<'lua> {
}
}
}
impl<'lua> PartialEq for AnyUserData<'lua> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<'lua> AsRef<AnyUserData<'lua>> for AnyUserData<'lua> {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
+46
View File
@@ -2,6 +2,7 @@ use std::iter::{self, FromIterator};
use std::{slice, str, vec};
use crate::error::{Error, Result};
use crate::ffi;
use crate::function::Function;
use crate::lua::Lua;
use crate::string::String;
@@ -61,6 +62,51 @@ impl<'lua> Value<'lua> {
Value::UserData(_) | Value::Error(_) => "userdata",
}
}
/// Compares two values for equality.
///
/// Equality comparisons do not convert strings to numbers or vice versa.
/// Tables, Functions, Threads, and Userdata are compared by reference:
/// two objects are considered equal only if they are the same object.
///
/// If Tables or Userdata have `__eq` metamethod then mlua will try to invoke it.
/// The first value is checked first. If that value does not define a metamethod
/// for `__eq`, then mlua will check the second value.
/// Then mlua calls the metamethod with the two values as arguments, if found.
pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
match (self, other.as_ref()) {
(Value::Table(a), Value::Table(b)) => a.equals(b),
(Value::UserData(a), Value::UserData(b)) => a.equals(b),
_ => Ok(self == other.as_ref()),
}
}
}
impl<'lua> PartialEq for Value<'lua> {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Nil, Value::Nil) => true,
(Value::Boolean(a), Value::Boolean(b)) => a == b,
(Value::LightUserData(a), Value::LightUserData(b)) => a == b,
(Value::Integer(a), Value::Integer(b)) => *a == *b,
(Value::Integer(a), Value::Number(b)) => *a as ffi::lua_Number == *b,
(Value::Number(a), Value::Integer(b)) => *a == *b as ffi::lua_Number,
(Value::Number(a), Value::Number(b)) => *a == *b,
(Value::String(a), Value::String(b)) => a == b,
(Value::Table(a), Value::Table(b)) => a == b,
(Value::Function(a), Value::Function(b)) => a == b,
(Value::Thread(a), Value::Thread(b)) => a == b,
(Value::UserData(a), Value::UserData(b)) => a == b,
_ => false,
}
}
}
impl<'lua> AsRef<Value<'lua>> for Value<'lua> {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}
/// Trait for types convertible to `Value`.
-11
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use bstr::{BStr, BString};
use mlua::{Lua, Result};
+1 -1
View File
@@ -1,5 +1,5 @@
#[cfg(any(feature = "lua53", feature = "lua51"))]
#[test]
#[ignore]
fn test_compile_fail() {
let t = trybuild::TestCases::new();
t.compile_fail("tests/compile_fail/*.rs");
@@ -18,7 +18,8 @@ error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `std::marker::PhantomData<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
@@ -33,7 +34,8 @@ error[E0277]: the type `std::cell::UnsafeCell<isize>` may contain interior mutab
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<isize>`
= note: required because it appears within the type `std::cell::Cell<isize>`
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `std::marker::PhantomData<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
+4 -2
View File
@@ -20,7 +20,8 @@ error[E0277]: the type `std::cell::UnsafeCell<mlua::lua::ExtraData>` may contain
|
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `std::marker::PhantomData<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
@@ -37,7 +38,8 @@ error[E0277]: the type `std::cell::UnsafeCell<isize>` may contain interior mutab
= help: within `mlua::lua::Lua`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell<isize>`
= note: required because it appears within the type `std::cell::Cell<isize>`
= note: required because it appears within the type `std::cell::RefCell<mlua::lua::ExtraData>`
= note: required because it appears within the type `std::marker::PhantomData<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `std::marker::PhantomData<alloc::sync::ArcInner<std::cell::RefCell<mlua::lua::ExtraData>>>`
= note: required because it appears within the type `std::sync::Arc<std::cell::RefCell<mlua::lua::ExtraData>>`
= note: required because it appears within the type `mlua::lua::Lua`
= note: required because of the requirements on the impl of `std::panic::UnwindSafe` for `&mlua::lua::Lua`
-11
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use mlua::{Function, Lua, Result, String};
#[test]
+12 -23
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use std::sync::Arc;
use mlua::{Lua, Result, UserData};
@@ -16,6 +5,7 @@ use mlua::{Lua, Result, UserData};
#[test]
fn test_gc_control() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
#[cfg(any(feature = "lua53", feature = "lua52"))]
{
@@ -30,9 +20,8 @@ fn test_gc_control() -> Result<()> {
impl UserData for MyUserdata {}
let rc = Arc::new(());
lua.globals()
.set("userdata", lua.create_userdata(MyUserdata(rc.clone()))?)?;
lua.globals().raw_remove("userdata")?;
globals.set("userdata", lua.create_userdata(MyUserdata(rc.clone()))?)?;
globals.raw_remove("userdata")?;
assert_eq!(Arc::strong_count(&rc), 2);
lua.gc_collect()?;
@@ -51,15 +40,15 @@ fn test_gc_error() {
match lua
.load(
r#"
val = nil
table = {}
setmetatable(table, {
__gc = function()
error("gcwascalled")
end
})
table = nil
collectgarbage("collect")
val = nil
table = {}
setmetatable(table, {
__gc = function()
error("gcwascalled")
end
})
table = nil
collectgarbage("collect")
"#,
)
.exec()
-11
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use std::cell::Cell;
use std::rc::Rc;
-11
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use std::borrow::Cow;
use mlua::{Lua, Result, String};
+57 -12
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use mlua::{Lua, Nil, Result, Table, Value};
#[test]
@@ -91,10 +80,32 @@ fn test_table() -> Result<()> {
globals.set("table4", lua.create_sequence_from(vec![1, 2, 3, 4, 5])?)?;
let table4 = globals.get::<_, Table>("table4")?;
assert_eq!(
table4.pairs().collect::<Result<Vec<(i64, i64)>>>()?,
table4
.clone()
.pairs()
.collect::<Result<Vec<(i64, i64)>>>()?,
vec![(1, 1), (2, 2), (3, 3), (4, 4), (5, 5)]
);
table4.raw_insert(4, 35)?;
table4.raw_insert(7, 7)?;
assert_eq!(
table4
.clone()
.pairs()
.collect::<Result<Vec<(i64, i64)>>>()?,
vec![(1, 1), (2, 2), (3, 3), (4, 35), (5, 4), (6, 5), (7, 7)]
);
table4.raw_remove(1)?;
assert_eq!(
table4
.clone()
.pairs()
.collect::<Result<Vec<(i64, i64)>>>()?,
vec![(1, 2), (2, 3), (3, 35), (4, 4), (5, 5), (6, 7)]
);
Ok(())
}
@@ -148,6 +159,40 @@ fn test_metatable() -> Result<()> {
Ok(())
}
#[test]
fn test_table_eq() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
lua.load(
r#"
table1 = {1}
table2 = {1}
table3 = table1
table4 = {1}
setmetatable(table4, {
__eq = function(a, b) return a[1] == b[1] end
})
"#,
)
.exec()?;
let table1 = globals.get::<_, Table>("table1")?;
let table2 = globals.get::<_, Table>("table2")?;
let table3 = globals.get::<_, Table>("table3")?;
let table4 = globals.get::<_, Table>("table4")?;
assert!(table1 != table2);
assert!(!table1.equals(&table2)?);
assert!(table1 == table3);
assert!(table1.equals(&table3)?);
assert!(table1 != table4);
assert!(table1.equals(&table4)?);
Ok(())
}
#[test]
fn test_table_error() -> Result<()> {
let lua = Lua::new();
+6 -17
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use std::iter::FromIterator;
use std::panic::catch_unwind;
use std::sync::Arc;
@@ -92,13 +81,13 @@ fn test_lua_multi() -> Result<()> {
lua.load(
r#"
function concat(arg1, arg2)
return arg1 .. arg2
end
function concat(arg1, arg2)
return arg1 .. arg2
end
function mreturn()
return 1, 2, 3, 4, 5, 6
end
function mreturn()
return 1, 2, 3, 4, 5, 6
end
"#,
)
.exec()?;
+10 -21
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use std::panic::catch_unwind;
use mlua::{Error, Function, Lua, Result, Thread, ThreadStatus};
@@ -20,13 +9,13 @@ fn test_thread() -> Result<()> {
let thread = lua.create_thread(
lua.load(
r#"
function (s)
local sum = s
for i = 1,4 do
sum = sum + coroutine.yield(sum)
end
return sum
function (s)
local sum = s
for i = 1,4 do
sum = sum + coroutine.yield(sum)
end
return sum
end
"#,
)
.eval()?,
@@ -47,11 +36,11 @@ fn test_thread() -> Result<()> {
let accumulate = lua.create_thread(
lua.load(
r#"
function (sum)
while true do
sum = sum + coroutine.yield(sum)
end
function (sum)
while true do
sum = sum + coroutine.yield(sum)
end
end
"#,
)
.eval::<Function>()?,
-11
View File
@@ -1,14 +1,3 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use std::os::raw::c_void;
use mlua::{Function, LightUserData, Lua, Result};
+29 -29
View File
@@ -1,19 +1,8 @@
#![cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
feature(link_args)
)]
#[cfg_attr(
all(feature = "luajit", target_os = "macos", target_arch = "x86_64"),
link_args = "-pagezero_size 10000 -image_base 100000000"
)]
extern "system" {}
use std::sync::Arc;
use mlua::{
AnyUserData, ExternalError, Function, Lua, MetaMethod, Result, String, UserData,
UserDataMethods,
UserDataMethods, Value,
};
#[test]
@@ -96,6 +85,9 @@ fn test_metamethods() -> Result<()> {
MetaMethod::Sub,
|_, (lhs, rhs): (MyUserData, MyUserData)| Ok(MyUserData(lhs.0 - rhs.0)),
);
methods.add_meta_function(MetaMethod::Eq, |_, (lhs, rhs): (MyUserData, MyUserData)| {
Ok(lhs.0 == rhs.0)
});
methods.add_meta_method(MetaMethod::Index, |_, data, index: String| {
if index.to_str()? == "inner" {
Ok(data.0)
@@ -104,7 +96,7 @@ fn test_metamethods() -> Result<()> {
}
});
#[cfg(any(feature = "lua53", feature = "lua52"))]
methods.add_meta_method(MetaMethod::IPairs, |lua, data, ()| {
methods.add_meta_method(MetaMethod::Pairs, |lua, data, ()| {
use std::iter::FromIterator;
let stateless_iter = lua.create_function(|_, (data, i): (MyUserData, i64)| {
let i = i + 1;
@@ -122,18 +114,19 @@ fn test_metamethods() -> Result<()> {
let globals = lua.globals();
globals.set("userdata1", MyUserData(7))?;
globals.set("userdata2", MyUserData(3))?;
globals.set("userdata3", MyUserData(3))?;
assert_eq!(
lua.load("userdata1 + userdata2").eval::<MyUserData>()?.0,
10
);
#[cfg(any(feature = "lua53", feature = "lua52"))]
let ipairs_it = {
let pairs_it = {
lua.load(
r#"
function ipairs_it()
function pairs_it()
local r = 0
for i, v in ipairs(userdata1) do
for i, v in pairs(userdata1) do
r = r + v
end
return r
@@ -141,16 +134,23 @@ fn test_metamethods() -> Result<()> {
"#,
)
.exec()?;
globals.get::<_, Function>("ipairs_it")?
globals.get::<_, Function>("pairs_it")?
};
assert_eq!(lua.load("userdata1 - userdata2").eval::<MyUserData>()?.0, 4);
assert_eq!(lua.load("userdata1:get()").eval::<i64>()?, 7);
assert_eq!(lua.load("userdata2.inner").eval::<i64>()?, 3);
#[cfg(any(feature = "lua53", feature = "lua52"))]
assert_eq!(ipairs_it.call::<_, i64>(())?, 28);
assert_eq!(pairs_it.call::<_, i64>(())?, 28);
assert!(lua.load("userdata2.nonexist_field").eval::<()>().is_err());
let userdata2: Value = globals.get("userdata2")?;
let userdata3: Value = globals.get("userdata3")?;
assert!(lua.load("userdata2 == userdata3").eval::<bool>()?);
assert!(userdata2 != userdata3); // because references are differ
assert!(userdata2.equals(userdata3)?);
Ok(())
}
@@ -175,18 +175,18 @@ fn test_gc_userdata() -> Result<()> {
assert!(lua
.load(
r#"
local tbl = setmetatable({
userdata = userdata
}, { __gc = function(self)
-- resurrect userdata
hatch = self.userdata
end })
local tbl = setmetatable({
userdata = userdata
}, { __gc = function(self)
-- resurrect userdata
hatch = self.userdata
end })
tbl = nil
userdata = nil -- make table and userdata collectable
collectgarbage("collect")
hatch:access()
"#
tbl = nil
userdata = nil -- make table and userdata collectable
collectgarbage("collect")
hatch:access()
"#
)
.exec()
.is_err());
+57
View File
@@ -0,0 +1,57 @@
use mlua::{Lua, Result, Value};
#[test]
fn test_value_eq() -> Result<()> {
let lua = Lua::new();
let globals = lua.globals();
lua.load(
r#"
table1 = {1}
table2 = {1}
string1 = "hello"
string2 = "hello"
num1 = 1
num2 = 1.0
num3 = "1"
func1 = function() end
func2 = func1
func3 = function() end
thread1 = coroutine.create(function() end)
thread2 = thread1
setmetatable(table1, {
__eq = function(a, b) return a[1] == b[1] end
})
"#,
)
.exec()?;
let table1: Value = globals.get("table1")?;
let table2: Value = globals.get("table2")?;
let string1: Value = globals.get("string1")?;
let string2: Value = globals.get("string2")?;
let num1: Value = globals.get("num1")?;
let num2: Value = globals.get("num2")?;
let num3: Value = globals.get("num3")?;
let func1: Value = globals.get("func1")?;
let func2: Value = globals.get("func2")?;
let func3: Value = globals.get("func3")?;
let thread1: Value = globals.get("thread1")?;
let thread2: Value = globals.get("thread2")?;
assert!(table1 != table2);
assert!(table1.equals(table2)?);
assert!(string1 == string2);
assert!(string1.equals(string2)?);
assert!(num1 == num2);
assert!(num1.equals(num2)?);
assert!(num1 != num3);
assert!(func1 == func2);
assert!(func1 != func3);
assert!(!func1.equals(func3)?);
assert!(thread1 == thread2);
assert!(thread1.equals(thread2)?);
Ok(())
}