Something went wrong. Try again.
Personal fork of prime agent. Mirror of: https://mnemosyne.sigilzero.dev/sigilzero/prime-agent
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215use 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 conveniencepub(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 = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))) )] IDiv, /// The bitwise AND (&) operator. #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))) )] BAnd, /// The bitwise OR (|) operator. #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))) )] BOr, /// The bitwise XOR (binary ~) operator. #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))) )] BXor, /// The bitwise NOT (unary ~) operator. #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))) )] BNot, /// The bitwise left shift (<<) operator. #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))) )] Shl, /// The bitwise right shift (>>) operator. #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] #[cfg_attr( docsrs, doc(cfg(any(feature = "lua55", 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 = "lua55", feature = "lua54", feature = "lua53", feature = "lua52", feature = "luajit52" ))] #[cfg_attr( docsrs, doc(cfg(any( feature = "lua55", 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(any(feature = "lua55", feature = "lua54"))] #[cfg_attr(docsrs, doc(cfg(any(feature = "lua55", 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 = "lua55", feature = "lua54", feature = "lua53", feature = "luau"))] MetaMethod::IDiv => "__idiv", #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] MetaMethod::BAnd => "__band", #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] MetaMethod::BOr => "__bor", #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] MetaMethod::BXor => "__bxor", #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] MetaMethod::BNot => "__bnot", #[cfg(any(feature = "lua55", feature = "lua54", feature = "lua53"))] MetaMethod::Shl => "__shl", #[cfg(any(feature = "lua55", 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 = "lua55", 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(any(feature = "lua55", 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>()) }
/// Checks whether the type of this userdata is a [proxy object] for `T`. /// /// [proxy object]: crate::Lua::create_proxy #[inline] pub fn is_proxy<T: 'static>(&self) -> bool { self.is::<UserDataProxy<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);}