mirror of
https://github.com/mlua-rs/mlua
synced 2026-06-08 16:05:43 +00:00
1152519074
They will allow implementing userdata methods that can be called only once, destructing userdata instance during the call.
1168 lines
43 KiB
Rust
1168 lines
43 KiB
Rust
use std::any::TypeId;
|
|
use std::ffi::CStr;
|
|
use std::fmt;
|
|
use std::hash::Hash;
|
|
use std::os::raw::{c_char, c_void};
|
|
use std::string::String as StdString;
|
|
|
|
use crate::error::{Error, Result};
|
|
use crate::function::Function;
|
|
use crate::state::Lua;
|
|
use crate::string::String;
|
|
use crate::table::{Table, TablePairs};
|
|
use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti};
|
|
use crate::types::{MaybeSend, ValueRef};
|
|
use crate::util::{check_stack, get_userdata, push_string, short_type_name, take_userdata, StackGuard};
|
|
use crate::value::Value;
|
|
|
|
#[cfg(feature = "async")]
|
|
use std::future::Future;
|
|
|
|
#[cfg(feature = "serde")]
|
|
use {
|
|
serde::ser::{self, Serialize, Serializer},
|
|
std::result::Result as StdResult,
|
|
};
|
|
|
|
// Re-export for convenience
|
|
pub(crate) use cell::UserDataStorage;
|
|
pub use r#ref::{UserDataRef, UserDataRefMut};
|
|
pub use registry::UserDataRegistry;
|
|
pub(crate) use registry::{RawUserDataRegistry, UserDataProxy};
|
|
pub(crate) use util::{
|
|
borrow_userdata_scoped, borrow_userdata_scoped_mut, collect_userdata, init_userdata_metatable,
|
|
TypeIdHints,
|
|
};
|
|
|
|
/// Kinds of metamethods that can be overridden.
|
|
///
|
|
/// Currently, this mechanism does not allow overriding the `__gc` metamethod, since there is
|
|
/// generally no need to do so: [`UserData`] implementors can instead just implement `Drop`.
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
|
#[non_exhaustive]
|
|
pub enum MetaMethod {
|
|
/// The `+` operator.
|
|
Add,
|
|
/// The `-` operator.
|
|
Sub,
|
|
/// The `*` operator.
|
|
Mul,
|
|
/// The `/` operator.
|
|
Div,
|
|
/// The `%` operator.
|
|
Mod,
|
|
/// The `^` operator.
|
|
Pow,
|
|
/// The unary minus (`-`) operator.
|
|
Unm,
|
|
/// The floor division (//) operator.
|
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))))]
|
|
IDiv,
|
|
/// The bitwise AND (&) operator.
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
|
BAnd,
|
|
/// The bitwise OR (|) operator.
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
|
BOr,
|
|
/// The bitwise XOR (binary ~) operator.
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
|
BXor,
|
|
/// The bitwise NOT (unary ~) operator.
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
|
BNot,
|
|
/// The bitwise left shift (<<) operator.
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
|
Shl,
|
|
/// The bitwise right shift (>>) operator.
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua54", feature = "lua53"))))]
|
|
Shr,
|
|
/// The string concatenation operator `..`.
|
|
Concat,
|
|
/// The length operator `#`.
|
|
Len,
|
|
/// The `==` operator.
|
|
Eq,
|
|
/// The `<` operator.
|
|
Lt,
|
|
/// The `<=` operator.
|
|
Le,
|
|
/// Index access `obj[key]`.
|
|
Index,
|
|
/// Index write access `obj[key] = value`.
|
|
NewIndex,
|
|
/// The call "operator" `obj(arg1, args2, ...)`.
|
|
Call,
|
|
/// The `__tostring` metamethod.
|
|
///
|
|
/// This is not an operator, but will be called by methods such as `tostring` and `print`.
|
|
ToString,
|
|
/// The `__pairs` metamethod.
|
|
///
|
|
/// This is not an operator, but it will be called by the built-in `pairs` function.
|
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
|
#[cfg_attr(
|
|
docsrs,
|
|
doc(cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52")))
|
|
)]
|
|
Pairs,
|
|
/// The `__ipairs` metamethod.
|
|
///
|
|
/// This is not an operator, but it will be called by the built-in [`ipairs`] function.
|
|
///
|
|
/// [`ipairs`]: https://www.lua.org/manual/5.2/manual.html#pdf-ipairs
|
|
#[cfg(any(feature = "lua52", feature = "luajit52", doc))]
|
|
#[cfg_attr(docsrs, doc(cfg(any(feature = "lua52", feature = "luajit52"))))]
|
|
IPairs,
|
|
/// The `__iter` metamethod.
|
|
///
|
|
/// Executed before the iteration begins, and should return an iterator function like `next`
|
|
/// (or a custom one).
|
|
#[cfg(any(feature = "luau", doc))]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
|
|
Iter,
|
|
/// The `__close` metamethod.
|
|
///
|
|
/// Executed when a variable, that marked as to-be-closed, goes out of scope.
|
|
///
|
|
/// More information about to-be-closed variables can be found in the Lua 5.4
|
|
/// [documentation][lua_doc].
|
|
///
|
|
/// [lua_doc]: https://www.lua.org/manual/5.4/manual.html#3.3.8
|
|
#[cfg(feature = "lua54")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "lua54")))]
|
|
Close,
|
|
/// The `__name`/`__type` metafield.
|
|
///
|
|
/// This is not a function, but it's value can be used by `tostring` and `typeof` built-in
|
|
/// functions.
|
|
#[doc(hidden)]
|
|
Type,
|
|
}
|
|
|
|
impl PartialEq<MetaMethod> for &str {
|
|
fn eq(&self, other: &MetaMethod) -> bool {
|
|
*self == other.name()
|
|
}
|
|
}
|
|
|
|
impl PartialEq<MetaMethod> for StdString {
|
|
fn eq(&self, other: &MetaMethod) -> bool {
|
|
self == other.name()
|
|
}
|
|
}
|
|
|
|
impl fmt::Display for MetaMethod {
|
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(fmt, "{}", self.name())
|
|
}
|
|
}
|
|
|
|
impl MetaMethod {
|
|
/// Returns Lua metamethod name, usually prefixed by two underscores.
|
|
pub const fn name(self) -> &'static str {
|
|
match self {
|
|
MetaMethod::Add => "__add",
|
|
MetaMethod::Sub => "__sub",
|
|
MetaMethod::Mul => "__mul",
|
|
MetaMethod::Div => "__div",
|
|
MetaMethod::Mod => "__mod",
|
|
MetaMethod::Pow => "__pow",
|
|
MetaMethod::Unm => "__unm",
|
|
|
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "luau"))]
|
|
MetaMethod::IDiv => "__idiv",
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
MetaMethod::BAnd => "__band",
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
MetaMethod::BOr => "__bor",
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
MetaMethod::BXor => "__bxor",
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
MetaMethod::BNot => "__bnot",
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
MetaMethod::Shl => "__shl",
|
|
#[cfg(any(feature = "lua54", feature = "lua53"))]
|
|
MetaMethod::Shr => "__shr",
|
|
|
|
MetaMethod::Concat => "__concat",
|
|
MetaMethod::Len => "__len",
|
|
MetaMethod::Eq => "__eq",
|
|
MetaMethod::Lt => "__lt",
|
|
MetaMethod::Le => "__le",
|
|
MetaMethod::Index => "__index",
|
|
MetaMethod::NewIndex => "__newindex",
|
|
MetaMethod::Call => "__call",
|
|
MetaMethod::ToString => "__tostring",
|
|
|
|
#[cfg(any(feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52"))]
|
|
MetaMethod::Pairs => "__pairs",
|
|
#[cfg(any(feature = "lua52", feature = "luajit52"))]
|
|
MetaMethod::IPairs => "__ipairs",
|
|
#[cfg(feature = "luau")]
|
|
MetaMethod::Iter => "__iter",
|
|
|
|
#[cfg(feature = "lua54")]
|
|
MetaMethod::Close => "__close",
|
|
|
|
#[rustfmt::skip]
|
|
MetaMethod::Type => if cfg!(feature = "luau") { "__type" } else { "__name" },
|
|
}
|
|
}
|
|
|
|
pub(crate) const fn as_cstr(self) -> &'static CStr {
|
|
match self {
|
|
#[rustfmt::skip]
|
|
MetaMethod::Type => if cfg!(feature = "luau") { c"__type" } else { c"__name" },
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
pub(crate) fn validate(name: &str) -> Result<&str> {
|
|
match name {
|
|
"__gc" => Err(Error::MetaMethodRestricted(name.to_string())),
|
|
"__metatable" => Err(Error::MetaMethodRestricted(name.to_string())),
|
|
_ if name.starts_with("__mlua") => Err(Error::MetaMethodRestricted(name.to_string())),
|
|
name => Ok(name),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl AsRef<str> for MetaMethod {
|
|
fn as_ref(&self) -> &str {
|
|
self.name()
|
|
}
|
|
}
|
|
|
|
impl From<MetaMethod> for StdString {
|
|
#[inline]
|
|
fn from(method: MetaMethod) -> Self {
|
|
method.name().to_owned()
|
|
}
|
|
}
|
|
|
|
/// Method registry for [`UserData`] implementors.
|
|
pub trait UserDataMethods<T> {
|
|
/// Add a regular method which accepts a `&T` as the first parameter.
|
|
///
|
|
/// Regular methods are implemented by overriding the `__index` metamethod and returning the
|
|
/// accessed method. This allows them to be used with the expected `userdata:method()` syntax.
|
|
///
|
|
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
|
/// be used as a fall-back if no regular method is found.
|
|
fn add_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a regular method which accepts a `&mut T` as the first parameter.
|
|
///
|
|
/// Refer to [`add_method`] for more information about the implementation.
|
|
///
|
|
/// [`add_method`]: UserDataMethods::add_method
|
|
fn add_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a method which accepts `T` as the first parameter.
|
|
///
|
|
/// The userdata `T` will be moved out of the userdata container. This is useful for
|
|
/// methods that need to consume the userdata.
|
|
///
|
|
/// The method can be called only once per userdata instance, subsequent calls will result in a
|
|
/// [`Error::UserDataDestructed`] error.
|
|
#[doc(hidden)]
|
|
fn add_method_once<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
T: 'static,
|
|
M: Fn(&Lua, T, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti,
|
|
{
|
|
let name = name.into();
|
|
let method_name = format!("{}.{name}", short_type_name::<T>());
|
|
self.add_function(name, move |lua, (ud, args): (AnyUserData, A)| {
|
|
let this = (ud.take()).map_err(|err| Error::bad_self_argument(&method_name, err))?;
|
|
method(lua, this, args)
|
|
});
|
|
}
|
|
|
|
/// Add an async method which accepts a `&T` as the first parameter and returns [`Future`].
|
|
///
|
|
/// Refer to [`add_method`] for more information about the implementation.
|
|
///
|
|
/// [`add_method`]: UserDataMethods::add_method
|
|
#[cfg(feature = "async")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
|
fn add_async_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
T: 'static,
|
|
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add an async method which accepts a `&mut T` as the first parameter and returns [`Future`].
|
|
///
|
|
/// Refer to [`add_method`] for more information about the implementation.
|
|
///
|
|
/// [`add_method`]: UserDataMethods::add_method
|
|
#[cfg(feature = "async")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
|
fn add_async_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
T: 'static,
|
|
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add an async method which accepts a `T` as the first parameter and returns [`Future`].
|
|
///
|
|
/// The userdata `T` will be moved out of the userdata container. This is useful for
|
|
/// methods that need to consume the userdata.
|
|
///
|
|
/// The method can be called only once per userdata instance, subsequent calls will result in a
|
|
/// [`Error::UserDataDestructed`] error.
|
|
#[cfg(feature = "async")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
|
#[doc(hidden)]
|
|
fn add_async_method_once<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
T: 'static,
|
|
M: Fn(Lua, T, A) -> MR + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
|
R: IntoLuaMulti,
|
|
{
|
|
let name = name.into();
|
|
let method_name = format!("{}.{name}", short_type_name::<T>());
|
|
self.add_async_function(name, move |lua, (ud, args): (AnyUserData, A)| {
|
|
match (ud.take()).map_err(|err| Error::bad_self_argument(&method_name, err)) {
|
|
Ok(this) => either::Either::Left(method(lua, this, args)),
|
|
Err(err) => either::Either::Right(async move { Err(err) }),
|
|
}
|
|
});
|
|
}
|
|
|
|
/// Add a regular method as a function which accepts generic arguments.
|
|
///
|
|
/// The first argument will be a [`AnyUserData`] of type `T` if the method is called with Lua
|
|
/// method syntax: `my_userdata:my_method(arg1, arg2)`, or it is passed in as the first
|
|
/// argument: `my_userdata.my_method(my_userdata, arg1, arg2)`.
|
|
fn add_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
|
where
|
|
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a regular method as a mutable function which accepts generic arguments.
|
|
///
|
|
/// This is a version of [`add_function`] that accepts a `FnMut` argument.
|
|
///
|
|
/// [`add_function`]: UserDataMethods::add_function
|
|
fn add_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
|
where
|
|
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a regular method as an async function which accepts generic arguments and returns
|
|
/// [`Future`].
|
|
///
|
|
/// This is an async version of [`add_function`].
|
|
///
|
|
/// [`add_function`]: UserDataMethods::add_function
|
|
#[cfg(feature = "async")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
|
fn add_async_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
|
where
|
|
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a metamethod which accepts a `&T` as the first parameter.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// This can cause an error with certain binary metamethods that can trigger if only the right
|
|
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
|
///
|
|
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
|
fn add_meta_method<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
M: Fn(&Lua, &T, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a metamethod as a function which accepts a `&mut T` as the first parameter.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// This can cause an error with certain binary metamethods that can trigger if only the right
|
|
/// side has a metatable. To prevent this, use [`add_meta_function`].
|
|
///
|
|
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
|
fn add_meta_method_mut<M, A, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
M: FnMut(&Lua, &mut T, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add an async metamethod which accepts a `&T` as the first parameter and returns [`Future`].
|
|
///
|
|
/// This is an async version of [`add_meta_method`].
|
|
///
|
|
/// [`add_meta_method`]: UserDataMethods::add_meta_method
|
|
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
|
#[cfg_attr(
|
|
docsrs,
|
|
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
|
)]
|
|
fn add_async_meta_method<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
T: 'static,
|
|
M: Fn(Lua, UserDataRef<T>, A) -> MR + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add an async metamethod which accepts a `&mut T` as the first parameter and returns
|
|
/// [`Future`].
|
|
///
|
|
/// This is an async version of [`add_meta_method_mut`].
|
|
///
|
|
/// [`add_meta_method_mut`]: UserDataMethods::add_meta_method_mut
|
|
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
|
|
fn add_async_meta_method_mut<M, A, MR, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
T: 'static,
|
|
M: Fn(Lua, UserDataRefMut<T>, A) -> MR + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
MR: Future<Output = Result<R>> + MaybeSend + 'static,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a metamethod which accepts generic arguments.
|
|
///
|
|
/// Metamethods for binary operators can be triggered if either the left or right argument to
|
|
/// the binary operator has a metatable, so the first argument here is not necessarily a
|
|
/// userdata of type `T`.
|
|
fn add_meta_function<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
|
where
|
|
F: Fn(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a metamethod as a mutable function which accepts generic arguments.
|
|
///
|
|
/// This is a version of [`add_meta_function`] that accepts a `FnMut` argument.
|
|
///
|
|
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
|
fn add_meta_function_mut<F, A, R>(&mut self, name: impl Into<StdString>, function: F)
|
|
where
|
|
F: FnMut(&Lua, A) -> Result<R> + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
R: IntoLuaMulti;
|
|
|
|
/// Add a metamethod which accepts generic arguments and returns [`Future`].
|
|
///
|
|
/// This is an async version of [`add_meta_function`].
|
|
///
|
|
/// [`add_meta_function`]: UserDataMethods::add_meta_function
|
|
#[cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau"))))]
|
|
#[cfg_attr(
|
|
docsrs,
|
|
doc(cfg(all(feature = "async", not(any(feature = "lua51", feature = "luau")))))
|
|
)]
|
|
fn add_async_meta_function<F, A, FR, R>(&mut self, name: impl Into<StdString>, function: F)
|
|
where
|
|
F: Fn(Lua, A) -> FR + MaybeSend + 'static,
|
|
A: FromLuaMulti,
|
|
FR: Future<Output = Result<R>> + MaybeSend + 'static,
|
|
R: IntoLuaMulti;
|
|
}
|
|
|
|
/// Field registry for [`UserData`] implementors.
|
|
pub trait UserDataFields<T> {
|
|
/// Add a static field to the [`UserData`].
|
|
///
|
|
/// Static fields are implemented by updating the `__index` metamethod and returning the
|
|
/// accessed field. This allows them to be used with the expected `userdata.field` syntax.
|
|
///
|
|
/// Static fields are usually shared between all instances of the [`UserData`] of the same type.
|
|
///
|
|
/// If `add_meta_method` is used to set the `__index` metamethod, it will
|
|
/// be used as a fall-back if no regular field or method are found.
|
|
fn add_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
|
where
|
|
V: IntoLua + 'static;
|
|
|
|
/// Add a regular field getter as a method which accepts a `&T` as the parameter.
|
|
///
|
|
/// Regular field getters are implemented by overriding the `__index` metamethod and returning
|
|
/// the accessed field. This allows them to be used with the expected `userdata.field` syntax.
|
|
///
|
|
/// If `add_meta_method` is used to set the `__index` metamethod, the `__index` metamethod will
|
|
/// be used as a fall-back if no regular field or method are found.
|
|
fn add_field_method_get<M, R>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
M: Fn(&Lua, &T) -> Result<R> + MaybeSend + 'static,
|
|
R: IntoLua;
|
|
|
|
/// Add a regular field setter as a method which accepts a `&mut T` as the first parameter.
|
|
///
|
|
/// Regular field setters are implemented by overriding the `__newindex` metamethod and setting
|
|
/// the accessed field. This allows them to be used with the expected `userdata.field = value`
|
|
/// syntax.
|
|
///
|
|
/// If `add_meta_method` is used to set the `__newindex` metamethod, the `__newindex` metamethod
|
|
/// will be used as a fall-back if no regular field is found.
|
|
fn add_field_method_set<M, A>(&mut self, name: impl Into<StdString>, method: M)
|
|
where
|
|
M: FnMut(&Lua, &mut T, A) -> Result<()> + MaybeSend + 'static,
|
|
A: FromLua;
|
|
|
|
/// Add a regular field getter as a function which accepts a generic [`AnyUserData`] of type `T`
|
|
/// argument.
|
|
fn add_field_function_get<F, R>(&mut self, name: impl Into<StdString>, function: F)
|
|
where
|
|
F: Fn(&Lua, AnyUserData) -> Result<R> + MaybeSend + 'static,
|
|
R: IntoLua;
|
|
|
|
/// Add a regular field setter as a function which accepts a generic [`AnyUserData`] of type `T`
|
|
/// first argument.
|
|
fn add_field_function_set<F, A>(&mut self, name: impl Into<StdString>, function: F)
|
|
where
|
|
F: FnMut(&Lua, AnyUserData, A) -> Result<()> + MaybeSend + 'static,
|
|
A: FromLua;
|
|
|
|
/// Add a metatable field.
|
|
///
|
|
/// This will initialize the metatable field with `value` on [`UserData`] creation.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
|
/// like `__gc` or `__metatable`.
|
|
fn add_meta_field<V>(&mut self, name: impl Into<StdString>, value: V)
|
|
where
|
|
V: IntoLua + 'static;
|
|
|
|
/// Add a metatable field computed from `f`.
|
|
///
|
|
/// This will initialize the metatable field from `f` on [`UserData`] creation.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// `mlua` will trigger an error on an attempt to define a protected metamethod,
|
|
/// like `__gc` or `__metatable`.
|
|
fn add_meta_field_with<F, R>(&mut self, name: impl Into<StdString>, f: F)
|
|
where
|
|
F: FnOnce(&Lua) -> Result<R> + 'static,
|
|
R: IntoLua;
|
|
}
|
|
|
|
/// Trait for custom userdata types.
|
|
///
|
|
/// By implementing this trait, a struct becomes eligible for use inside Lua code.
|
|
///
|
|
/// Implementation of [`IntoLua`] is automatically provided, [`FromLua`] needs to be implemented
|
|
/// manually.
|
|
///
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// # use mlua::{Lua, Result, UserData};
|
|
/// # fn main() -> Result<()> {
|
|
/// # let lua = Lua::new();
|
|
/// struct MyUserData;
|
|
///
|
|
/// impl UserData for MyUserData {}
|
|
///
|
|
/// // `MyUserData` now implements `IntoLua`:
|
|
/// lua.globals().set("myobject", MyUserData)?;
|
|
///
|
|
/// lua.load("assert(type(myobject) == 'userdata')").exec()?;
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
///
|
|
/// Custom fields, methods and operators can be provided by implementing `add_fields` or
|
|
/// `add_methods` (refer to [`UserDataFields`] and [`UserDataMethods`] for more information):
|
|
///
|
|
/// ```
|
|
/// # use mlua::{Lua, MetaMethod, Result, UserData, UserDataFields, UserDataMethods};
|
|
/// # fn main() -> Result<()> {
|
|
/// # let lua = Lua::new();
|
|
/// struct MyUserData(i32);
|
|
///
|
|
/// impl UserData for MyUserData {
|
|
/// fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {
|
|
/// fields.add_field_method_get("val", |_, this| Ok(this.0));
|
|
/// }
|
|
///
|
|
/// fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
|
|
/// methods.add_method_mut("add", |_, mut this, value: i32| {
|
|
/// this.0 += value;
|
|
/// Ok(())
|
|
/// });
|
|
///
|
|
/// methods.add_meta_method(MetaMethod::Add, |_, this, value: i32| {
|
|
/// Ok(this.0 + value)
|
|
/// });
|
|
/// }
|
|
/// }
|
|
///
|
|
/// lua.globals().set("myobject", MyUserData(123))?;
|
|
///
|
|
/// lua.load(r#"
|
|
/// assert(myobject.val == 123)
|
|
/// myobject:add(7)
|
|
/// assert(myobject.val == 130)
|
|
/// assert(myobject + 10 == 140)
|
|
/// "#).exec()?;
|
|
/// # Ok(())
|
|
/// # }
|
|
/// ```
|
|
pub trait UserData: Sized {
|
|
/// Adds custom fields specific to this userdata.
|
|
#[allow(unused_variables)]
|
|
fn add_fields<F: UserDataFields<Self>>(fields: &mut F) {}
|
|
|
|
/// Adds custom methods and operators specific to this userdata.
|
|
#[allow(unused_variables)]
|
|
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {}
|
|
|
|
/// Registers this type for use in Lua.
|
|
///
|
|
/// This method is responsible for calling `add_fields` and `add_methods` on the provided
|
|
/// [`UserDataRegistry`].
|
|
fn register(registry: &mut UserDataRegistry<Self>) {
|
|
Self::add_fields(registry);
|
|
Self::add_methods(registry);
|
|
}
|
|
}
|
|
|
|
/// Handle to an internal Lua userdata for any type that implements [`UserData`].
|
|
///
|
|
/// Similar to [`std::any::Any`], this provides an interface for dynamic type checking via the
|
|
/// [`is`] and [`borrow`] methods.
|
|
///
|
|
/// # Note
|
|
///
|
|
/// This API should only be used when necessary. Implementing [`UserData`] already allows defining
|
|
/// methods which check the type and acquire a borrow behind the scenes.
|
|
///
|
|
/// [`is`]: crate::AnyUserData::is
|
|
/// [`borrow`]: crate::AnyUserData::borrow
|
|
#[derive(Clone, Debug, PartialEq)]
|
|
pub struct AnyUserData(pub(crate) ValueRef);
|
|
|
|
impl AnyUserData {
|
|
/// Checks whether the type of this userdata is `T`.
|
|
#[inline]
|
|
pub fn is<T: 'static>(&self) -> bool {
|
|
let type_id = self.type_id();
|
|
// We do not use wrapped types here, rather prefer to check the "real" type of the userdata
|
|
matches!(type_id, Some(type_id) if type_id == TypeId::of::<T>())
|
|
}
|
|
|
|
/// Borrow this userdata immutably if it is of type `T`.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns a [`UserDataBorrowError`] if the userdata is already mutably borrowed.
|
|
/// Returns a [`DataTypeMismatch`] if the userdata is not of type `T` or if it's
|
|
/// scoped.
|
|
///
|
|
/// [`UserDataBorrowError`]: crate::Error::UserDataBorrowError
|
|
/// [`DataTypeMismatch`]: crate::Error::UserDataTypeMismatch
|
|
#[inline]
|
|
pub fn borrow<T: 'static>(&self) -> Result<UserDataRef<T>> {
|
|
let lua = self.0.lua.lock();
|
|
unsafe { UserDataRef::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
|
|
}
|
|
|
|
/// Borrow this userdata immutably if it is of type `T`, passing the borrowed value
|
|
/// to the closure.
|
|
///
|
|
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
|
|
pub fn borrow_scoped<T: 'static, R>(&self, f: impl FnOnce(&T) -> R) -> Result<R> {
|
|
let lua = self.0.lua.lock();
|
|
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
|
let type_hints = TypeIdHints::new::<T>();
|
|
unsafe { borrow_userdata_scoped(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
|
|
}
|
|
|
|
/// Borrow this userdata mutably if it is of type `T`.
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns a [`UserDataBorrowMutError`] if the userdata cannot be mutably borrowed.
|
|
/// Returns a [`UserDataTypeMismatch`] if the userdata is not of type `T` or if it's
|
|
/// scoped.
|
|
///
|
|
/// [`UserDataBorrowMutError`]: crate::Error::UserDataBorrowMutError
|
|
/// [`UserDataTypeMismatch`]: crate::Error::UserDataTypeMismatch
|
|
#[inline]
|
|
pub fn borrow_mut<T: 'static>(&self) -> Result<UserDataRefMut<T>> {
|
|
let lua = self.0.lua.lock();
|
|
unsafe { UserDataRefMut::borrow_from_stack(&lua, lua.ref_thread(), self.0.index) }
|
|
}
|
|
|
|
/// Borrow this userdata mutably if it is of type `T`, passing the borrowed value
|
|
/// to the closure.
|
|
///
|
|
/// This method is the only way to borrow scoped userdata (created inside [`Lua::scope`]).
|
|
pub fn borrow_mut_scoped<T: 'static, R>(&self, f: impl FnOnce(&mut T) -> R) -> Result<R> {
|
|
let lua = self.0.lua.lock();
|
|
let type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
|
let type_hints = TypeIdHints::new::<T>();
|
|
unsafe { borrow_userdata_scoped_mut(lua.ref_thread(), self.0.index, type_id, type_hints, f) }
|
|
}
|
|
|
|
/// Takes the value out of this userdata.
|
|
///
|
|
/// Sets the special "destructed" metatable that prevents any further operations with this
|
|
/// userdata.
|
|
///
|
|
/// Keeps associated user values unchanged (they will be collected by Lua's GC).
|
|
pub fn take<T: 'static>(&self) -> Result<T> {
|
|
let lua = self.0.lua.lock();
|
|
match lua.get_userdata_ref_type_id(&self.0)? {
|
|
Some(type_id) if type_id == TypeId::of::<T>() => unsafe {
|
|
let ref_thread = lua.ref_thread();
|
|
if (*get_userdata::<UserDataStorage<T>>(ref_thread, self.0.index)).has_exclusive_access() {
|
|
take_userdata::<UserDataStorage<T>>(ref_thread, self.0.index).into_inner()
|
|
} else {
|
|
Err(Error::UserDataBorrowMutError)
|
|
}
|
|
},
|
|
_ => Err(Error::UserDataTypeMismatch),
|
|
}
|
|
}
|
|
|
|
/// Destroys this userdata.
|
|
///
|
|
/// This is similar to [`AnyUserData::take`], but it doesn't require a type.
|
|
///
|
|
/// This method works for non-scoped userdata only.
|
|
pub fn destroy(&self) -> Result<()> {
|
|
let lua = self.0.lua.lock();
|
|
let state = lua.state();
|
|
unsafe {
|
|
let _sg = StackGuard::new(state);
|
|
check_stack(state, 3)?;
|
|
|
|
lua.push_userdata_ref(&self.0)?;
|
|
protect_lua!(state, 1, 1, fn(state) {
|
|
if ffi::luaL_callmeta(state, -1, cstr!("__gc")) == 0 {
|
|
ffi::lua_pushboolean(state, 0);
|
|
}
|
|
})?;
|
|
if ffi::lua_isboolean(state, -1) != 0 && ffi::lua_toboolean(state, -1) != 0 {
|
|
return Ok(());
|
|
}
|
|
Err(Error::UserDataBorrowMutError)
|
|
}
|
|
}
|
|
|
|
/// Sets an associated value to this [`AnyUserData`].
|
|
///
|
|
/// The value may be any Lua value whatsoever, and can be retrieved with [`user_value`].
|
|
///
|
|
/// This is the same as calling [`set_nth_user_value`] with `n` set to 1.
|
|
///
|
|
/// [`user_value`]: AnyUserData::user_value
|
|
/// [`set_nth_user_value`]: AnyUserData::set_nth_user_value
|
|
#[inline]
|
|
pub fn set_user_value(&self, v: impl IntoLua) -> Result<()> {
|
|
self.set_nth_user_value(1, v)
|
|
}
|
|
|
|
/// Returns an associated value set by [`set_user_value`].
|
|
///
|
|
/// This is the same as calling [`nth_user_value`] with `n` set to 1.
|
|
///
|
|
/// [`set_user_value`]: AnyUserData::set_user_value
|
|
/// [`nth_user_value`]: AnyUserData::nth_user_value
|
|
#[inline]
|
|
pub fn user_value<V: FromLua>(&self) -> Result<V> {
|
|
self.nth_user_value(1)
|
|
}
|
|
|
|
/// Sets an associated `n`th value to this [`AnyUserData`].
|
|
///
|
|
/// The value may be any Lua value whatsoever, and can be retrieved with [`nth_user_value`].
|
|
/// `n` starts from 1 and can be up to 65535.
|
|
///
|
|
/// This is supported for all Lua versions using a wrapping table.
|
|
///
|
|
/// [`nth_user_value`]: AnyUserData::nth_user_value
|
|
pub fn set_nth_user_value(&self, n: usize, v: impl IntoLua) -> Result<()> {
|
|
if n < 1 || n > u16::MAX as usize {
|
|
return Err(Error::runtime("user value index out of bounds"));
|
|
}
|
|
|
|
let lua = self.0.lua.lock();
|
|
let state = lua.state();
|
|
unsafe {
|
|
let _sg = StackGuard::new(state);
|
|
check_stack(state, 5)?;
|
|
|
|
lua.push_userdata_ref(&self.0)?;
|
|
lua.push(v)?;
|
|
|
|
// Multiple (extra) user values are emulated by storing them in a table
|
|
protect_lua!(state, 2, 0, |state| {
|
|
if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
|
|
// Create a new table to use as uservalue
|
|
ffi::lua_pop(state, 1);
|
|
ffi::lua_newtable(state);
|
|
ffi::lua_pushvalue(state, -1);
|
|
ffi::lua_setuservalue(state, -4);
|
|
}
|
|
ffi::lua_pushvalue(state, -2);
|
|
ffi::lua_rawseti(state, -2, n as ffi::lua_Integer);
|
|
})?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Returns an associated `n`th value set by [`set_nth_user_value`].
|
|
///
|
|
/// `n` starts from 1 and can be up to 65535.
|
|
///
|
|
/// This is supported for all Lua versions using a wrapping table.
|
|
///
|
|
/// [`set_nth_user_value`]: AnyUserData::set_nth_user_value
|
|
pub fn nth_user_value<V: FromLua>(&self, n: usize) -> Result<V> {
|
|
if n < 1 || n > u16::MAX as usize {
|
|
return Err(Error::runtime("user value index out of bounds"));
|
|
}
|
|
|
|
let lua = self.0.lua.lock();
|
|
let state = lua.state();
|
|
unsafe {
|
|
let _sg = StackGuard::new(state);
|
|
check_stack(state, 4)?;
|
|
|
|
lua.push_userdata_ref(&self.0)?;
|
|
|
|
// Multiple (extra) user values are emulated by storing them in a table
|
|
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
|
|
return V::from_lua(Value::Nil, lua.lua());
|
|
}
|
|
ffi::lua_rawgeti(state, -1, n as ffi::lua_Integer);
|
|
|
|
V::from_lua(lua.pop_value(), lua.lua())
|
|
}
|
|
}
|
|
|
|
/// Sets an associated value to this [`AnyUserData`] by name.
|
|
///
|
|
/// The value can be retrieved with [`named_user_value`].
|
|
///
|
|
/// [`named_user_value`]: AnyUserData::named_user_value
|
|
pub fn set_named_user_value(&self, name: &str, v: impl IntoLua) -> Result<()> {
|
|
let lua = self.0.lua.lock();
|
|
let state = lua.state();
|
|
unsafe {
|
|
let _sg = StackGuard::new(state);
|
|
check_stack(state, 5)?;
|
|
|
|
lua.push_userdata_ref(&self.0)?;
|
|
lua.push(v)?;
|
|
|
|
// Multiple (extra) user values are emulated by storing them in a table
|
|
protect_lua!(state, 2, 0, |state| {
|
|
if ffi::lua_getuservalue(state, -2) != ffi::LUA_TTABLE {
|
|
// Create a new table to use as uservalue
|
|
ffi::lua_pop(state, 1);
|
|
ffi::lua_newtable(state);
|
|
ffi::lua_pushvalue(state, -1);
|
|
ffi::lua_setuservalue(state, -4);
|
|
}
|
|
ffi::lua_pushlstring(state, name.as_ptr() as *const c_char, name.len());
|
|
ffi::lua_pushvalue(state, -3);
|
|
ffi::lua_rawset(state, -3);
|
|
})?;
|
|
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Returns an associated value by name set by [`set_named_user_value`].
|
|
///
|
|
/// [`set_named_user_value`]: AnyUserData::set_named_user_value
|
|
pub fn named_user_value<V: FromLua>(&self, name: &str) -> Result<V> {
|
|
let lua = self.0.lua.lock();
|
|
let state = lua.state();
|
|
unsafe {
|
|
let _sg = StackGuard::new(state);
|
|
check_stack(state, 4)?;
|
|
|
|
lua.push_userdata_ref(&self.0)?;
|
|
|
|
// Multiple (extra) user values are emulated by storing them in a table
|
|
if ffi::lua_getuservalue(state, -1) != ffi::LUA_TTABLE {
|
|
return V::from_lua(Value::Nil, lua.lua());
|
|
}
|
|
push_string(state, name.as_bytes(), !lua.unlikely_memory_error())?;
|
|
ffi::lua_rawget(state, -2);
|
|
|
|
V::from_stack(-1, &lua)
|
|
}
|
|
}
|
|
|
|
/// Returns a metatable of this [`AnyUserData`].
|
|
///
|
|
/// Returned [`UserDataMetatable`] object wraps the original metatable and
|
|
/// provides safe access to its methods.
|
|
///
|
|
/// For `T: 'static` returned metatable is shared among all instances of type `T`.
|
|
#[inline]
|
|
pub fn metatable(&self) -> Result<UserDataMetatable> {
|
|
self.raw_metatable().map(UserDataMetatable)
|
|
}
|
|
|
|
/// Returns a raw metatable of this [`AnyUserData`].
|
|
fn raw_metatable(&self) -> Result<Table> {
|
|
let lua = self.0.lua.lock();
|
|
let ref_thread = lua.ref_thread();
|
|
unsafe {
|
|
// Check that userdata is registered and not destructed
|
|
// All registered userdata types have a non-empty metatable
|
|
let _type_id = lua.get_userdata_ref_type_id(&self.0)?;
|
|
|
|
ffi::lua_getmetatable(ref_thread, self.0.index);
|
|
Ok(Table(lua.pop_ref_thread()))
|
|
}
|
|
}
|
|
|
|
/// Converts this userdata to a generic C pointer.
|
|
///
|
|
/// There is no way to convert the pointer back to its original value.
|
|
///
|
|
/// Typically this function is used only for hashing and debug information.
|
|
#[inline]
|
|
pub fn to_pointer(&self) -> *const c_void {
|
|
self.0.to_pointer()
|
|
}
|
|
|
|
/// Returns [`TypeId`] of this userdata if it is registered and `'static`.
|
|
///
|
|
/// This method is not available for scoped userdata.
|
|
#[inline]
|
|
pub fn type_id(&self) -> Option<TypeId> {
|
|
let lua = self.0.lua.lock();
|
|
lua.get_userdata_ref_type_id(&self.0).ok().flatten()
|
|
}
|
|
|
|
/// Returns a type name of this userdata (from a metatable field).
|
|
///
|
|
/// If no type name is set, returns `None`.
|
|
pub fn type_name(&self) -> Result<Option<StdString>> {
|
|
let lua = self.0.lua.lock();
|
|
let state = lua.state();
|
|
unsafe {
|
|
let _sg = StackGuard::new(state);
|
|
check_stack(state, 3)?;
|
|
|
|
lua.push_userdata_ref(&self.0)?;
|
|
let protect = !lua.unlikely_memory_error();
|
|
let name_type = if protect {
|
|
protect_lua!(state, 1, 1, |state| {
|
|
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
|
})?
|
|
} else {
|
|
ffi::luaL_getmetafield(state, -1, MetaMethod::Type.as_cstr().as_ptr())
|
|
};
|
|
match name_type {
|
|
ffi::LUA_TSTRING => Ok(Some(String(lua.pop_ref()).to_str()?.to_owned())),
|
|
_ => Ok(None),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn equals(&self, other: &Self) -> Result<bool> {
|
|
// Uses lua_rawequal() under the hood
|
|
if self == other {
|
|
return Ok(true);
|
|
}
|
|
|
|
let mt = self.raw_metatable()?;
|
|
if mt != other.raw_metatable()? {
|
|
return Ok(false);
|
|
}
|
|
|
|
if mt.contains_key("__eq")? {
|
|
return mt.get::<Function>("__eq")?.call((self, other));
|
|
}
|
|
|
|
Ok(false)
|
|
}
|
|
|
|
/// Returns `true` if this [`AnyUserData`] is serializable (e.g. was created using
|
|
/// [`Lua::create_ser_userdata`]).
|
|
#[cfg(feature = "serde")]
|
|
pub(crate) fn is_serializable(&self) -> bool {
|
|
let lua = self.0.lua.lock();
|
|
let is_serializable = || unsafe {
|
|
// Userdata must be registered and not destructed
|
|
let _ = lua.get_userdata_ref_type_id(&self.0)?;
|
|
let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
|
|
Ok::<_, Error>((*ud).is_serializable())
|
|
};
|
|
is_serializable().unwrap_or(false)
|
|
}
|
|
}
|
|
|
|
/// Handle to a [`AnyUserData`] metatable.
|
|
#[derive(Clone, Debug)]
|
|
pub struct UserDataMetatable(pub(crate) Table);
|
|
|
|
impl UserDataMetatable {
|
|
/// Gets the value associated to `key` from the metatable.
|
|
///
|
|
/// If no value is associated to `key`, returns the `Nil` value.
|
|
/// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
|
|
pub fn get<V: FromLua>(&self, key: impl AsRef<str>) -> Result<V> {
|
|
self.0.raw_get(MetaMethod::validate(key.as_ref())?)
|
|
}
|
|
|
|
/// Sets a key-value pair in the metatable.
|
|
///
|
|
/// If the value is `Nil`, this will effectively remove the `key`.
|
|
/// Access to restricted metamethods such as `__gc` or `__metatable` will cause an error.
|
|
/// Setting `__index` or `__newindex` metamethods is also restricted because their values are
|
|
/// cached for `mlua` internal usage.
|
|
pub fn set(&self, key: impl AsRef<str>, value: impl IntoLua) -> Result<()> {
|
|
let key = MetaMethod::validate(key.as_ref())?;
|
|
// `__index` and `__newindex` cannot be changed in runtime, because values are cached
|
|
if key == MetaMethod::Index || key == MetaMethod::NewIndex {
|
|
return Err(Error::MetaMethodRestricted(key.to_string()));
|
|
}
|
|
self.0.raw_set(key, value)
|
|
}
|
|
|
|
/// Checks whether the metatable contains a non-nil value for `key`.
|
|
pub fn contains(&self, key: impl AsRef<str>) -> Result<bool> {
|
|
self.0.contains_key(MetaMethod::validate(key.as_ref())?)
|
|
}
|
|
|
|
/// Returns an iterator over the pairs of the metatable.
|
|
///
|
|
/// The pairs are wrapped in a [`Result`], since they are lazily converted to `V` type.
|
|
///
|
|
/// [`Result`]: crate::Result
|
|
pub fn pairs<V: FromLua>(&self) -> UserDataMetatablePairs<'_, V> {
|
|
UserDataMetatablePairs(self.0.pairs())
|
|
}
|
|
}
|
|
|
|
/// An iterator over the pairs of a [`AnyUserData`] metatable.
|
|
///
|
|
/// It skips restricted metamethods, such as `__gc` or `__metatable`.
|
|
///
|
|
/// This struct is created by the [`UserDataMetatable::pairs`] method.
|
|
pub struct UserDataMetatablePairs<'a, V>(TablePairs<'a, StdString, V>);
|
|
|
|
impl<V> Iterator for UserDataMetatablePairs<'_, V>
|
|
where
|
|
V: FromLua,
|
|
{
|
|
type Item = Result<(StdString, V)>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
loop {
|
|
match self.0.next()? {
|
|
Ok((key, value)) => {
|
|
// Skip restricted metamethods
|
|
if MetaMethod::validate(&key).is_ok() {
|
|
break Some(Ok((key, value)));
|
|
}
|
|
}
|
|
Err(e) => break Some(Err(e)),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "serde")]
|
|
impl Serialize for AnyUserData {
|
|
fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
let lua = self.0.lua.lock();
|
|
unsafe {
|
|
let _ = lua
|
|
.get_userdata_ref_type_id(&self.0)
|
|
.map_err(ser::Error::custom)?;
|
|
let ud = &*get_userdata::<UserDataStorage<()>>(lua.ref_thread(), self.0.index);
|
|
ud.serialize(serializer)
|
|
}
|
|
}
|
|
}
|
|
|
|
struct WrappedUserdata<F: FnOnce(&Lua) -> Result<AnyUserData>>(F);
|
|
|
|
impl AnyUserData {
|
|
/// Wraps any Rust type, returning an opaque type that implements [`IntoLua`] trait.
|
|
///
|
|
/// This function uses [`Lua::create_any_userdata`] under the hood.
|
|
pub fn wrap<T: MaybeSend + 'static>(data: T) -> impl IntoLua {
|
|
WrappedUserdata(move |lua| lua.create_any_userdata(data))
|
|
}
|
|
|
|
/// Wraps any Rust type that implements [`Serialize`], returning an opaque type that implements
|
|
/// [`IntoLua`] trait.
|
|
///
|
|
/// This function uses [`Lua::create_ser_any_userdata`] under the hood.
|
|
#[cfg(feature = "serde")]
|
|
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
|
|
pub fn wrap_ser<T: Serialize + MaybeSend + 'static>(data: T) -> impl IntoLua {
|
|
WrappedUserdata(move |lua| lua.create_ser_any_userdata(data))
|
|
}
|
|
}
|
|
|
|
impl<F> IntoLua for WrappedUserdata<F>
|
|
where
|
|
F: for<'l> FnOnce(&'l Lua) -> Result<AnyUserData>,
|
|
{
|
|
fn into_lua(self, lua: &Lua) -> Result<Value> {
|
|
(self.0)(lua).map(Value::UserData)
|
|
}
|
|
}
|
|
|
|
mod cell;
|
|
mod lock;
|
|
mod object;
|
|
mod r#ref;
|
|
mod registry;
|
|
mod util;
|
|
|
|
#[cfg(test)]
|
|
mod assertions {
|
|
use super::*;
|
|
|
|
#[cfg(not(feature = "send"))]
|
|
static_assertions::assert_not_impl_any!(AnyUserData: Send);
|
|
#[cfg(feature = "send")]
|
|
static_assertions::assert_impl_all!(AnyUserData: Send, Sync);
|
|
}
|