From 703601e34823cdedcb96f44956c93b401bf9a3da Mon Sep 17 00:00:00 2001 From: kyren Date: Tue, 4 Sep 2018 19:05:21 -0400 Subject: [PATCH] code re-org have slightly less pub(crate) items --- src/lib.rs | 4 +- src/lua.rs | 173 ++++++++++++++++- src/methods.rs | 502 ------------------------------------------------ src/scope.rs | 142 +++++++++++++- src/userdata.rs | 196 ++++++++++++++++++- 5 files changed, 504 insertions(+), 513 deletions(-) delete mode 100644 src/methods.rs diff --git a/src/lib.rs b/src/lib.rs index 32fe0cd..d7106cd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -51,7 +51,6 @@ mod macros; mod conversion; mod function; mod lua; -mod methods; mod multi; mod scope; mod string; @@ -68,14 +67,13 @@ mod tests; pub use error::{Error, ExternalError, ExternalResult, Result}; pub use function::Function; pub use lua::Lua; -pub use methods::{MetaMethod, UserDataMethods}; pub use multi::Variadic; pub use scope::Scope; pub use string::String; pub use table::{Table, TablePairs, TableSequence}; pub use thread::{Thread, ThreadStatus}; pub use types::{Integer, LightUserData, Number, RegistryKey}; -pub use userdata::{AnyUserData, UserData}; +pub use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; pub use value::{FromLua, FromLuaMulti, MultiValue, Nil, ToLua, ToLuaMulti, Value}; pub mod prelude; diff --git a/src/lua.rs b/src/lua.rs index 7c945ea..86aa1c2 100644 --- a/src/lua.rs +++ b/src/lua.rs @@ -4,6 +4,7 @@ use std::collections::HashMap; use std::ffi::CString; use std::marker::PhantomData; use std::os::raw::{c_char, c_int, c_void}; +use std::string::String as StdString; use std::sync::{Arc, Mutex}; use std::{mem, ptr, str}; @@ -12,13 +13,12 @@ use libc; use error::{Error, Result}; use ffi; use function::Function; -use methods::{meta_method_name, StaticUserDataMethods}; use scope::Scope; use string::String; use table::Table; use thread::Thread; use types::{Callback, Integer, LightUserData, LuaRef, Number, RegistryKey}; -use userdata::{AnyUserData, UserData}; +use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; use util::{ assert_stack, callback_error, check_stack, gc_guard, get_userdata, get_wrapped_error, init_error_metatables, init_userdata_metatable, main_state, pop_error, protect_lua, @@ -788,7 +788,7 @@ impl Lua { ffi::lua_newtable(state); })?; for (k, m) in methods.meta_methods { - push_string(self.state, meta_method_name(k))?; + push_string(self.state, k.name())?; self.push_value(Value::Function(self.create_callback(m)?)); protect_lua_closure(self.state, 3, 1, |state| { @@ -1061,3 +1061,170 @@ unsafe fn ref_stack_pop(extra: *mut ExtraData) -> c_int { } static FUNCTION_METATABLE_REGISTRY_KEY: u8 = 0; + +struct StaticUserDataMethods<'lua, T: 'static + UserData> { + methods: HashMap>, + meta_methods: HashMap>, + _type: PhantomData, +} + +impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> { + fn default() -> StaticUserDataMethods<'lua, T> { + StaticUserDataMethods { + methods: HashMap::new(), + meta_methods: HashMap::new(), + _type: PhantomData, + } + } +} + +impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> { + fn add_method(&mut self, name: &str, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, + { + self.methods + .insert(name.to_owned(), Self::box_method(method)); + } + + fn add_method_mut(&mut self, name: &str, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, + { + self.methods + .insert(name.to_owned(), Self::box_method_mut(method)); + } + + fn add_function(&mut self, name: &str, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + Fn(&'lua Lua, A) -> Result, + { + self.methods + .insert(name.to_owned(), Self::box_function(function)); + } + + fn add_function_mut(&mut self, name: &str, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + FnMut(&'lua Lua, A) -> Result, + { + self.methods + .insert(name.to_owned(), Self::box_function_mut(function)); + } + + fn add_meta_method(&mut self, meta: MetaMethod, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, + { + self.meta_methods.insert(meta, Self::box_method(method)); + } + + fn add_meta_method_mut(&mut self, meta: MetaMethod, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, + { + self.meta_methods.insert(meta, Self::box_method_mut(method)); + } + + fn add_meta_function(&mut self, meta: MetaMethod, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + Fn(&'lua Lua, A) -> Result, + { + self.meta_methods.insert(meta, Self::box_function(function)); + } + + fn add_meta_function_mut(&mut self, meta: MetaMethod, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + FnMut(&'lua Lua, A) -> Result, + { + self.meta_methods + .insert(meta, Self::box_function_mut(function)); + } +} + +impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { + fn box_method(method: M) -> Callback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, + { + Box::new(move |lua, mut args| { + if let Some(front) = args.pop_front() { + let userdata = AnyUserData::from_lua(front, lua)?; + let userdata = userdata.borrow::()?; + method(lua, &userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } else { + Err(Error::FromLuaConversionError { + from: "missing argument", + to: "userdata", + message: None, + }) + } + }) + } + + fn box_method_mut(method: M) -> Callback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, + { + let method = RefCell::new(method); + Box::new(move |lua, mut args| { + if let Some(front) = args.pop_front() { + let userdata = AnyUserData::from_lua(front, lua)?; + let mut userdata = userdata.borrow_mut::()?; + let mut method = method + .try_borrow_mut() + .map_err(|_| Error::RecursiveMutCallback)?; + (&mut *method)(lua, &mut userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + } else { + Err(Error::FromLuaConversionError { + from: "missing argument", + to: "userdata", + message: None, + }) + } + }) + } + + fn box_function(function: F) -> Callback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + Fn(&'lua Lua, A) -> Result, + { + Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)) + } + + fn box_function_mut(function: F) -> Callback<'lua, 'static> + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + FnMut(&'lua Lua, A) -> Result, + { + let function = RefCell::new(function); + Box::new(move |lua, args| { + let function = &mut *function + .try_borrow_mut() + .map_err(|_| Error::RecursiveMutCallback)?; + function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + }) + } +} diff --git a/src/methods.rs b/src/methods.rs deleted file mode 100644 index d953f96..0000000 --- a/src/methods.rs +++ /dev/null @@ -1,502 +0,0 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::marker::PhantomData; -use std::string::String as StdString; - -use error::{Error, Result}; -use lua::Lua; -use types::Callback; -use userdata::{AnyUserData, UserData}; -use value::{FromLua, FromLuaMulti, MultiValue, ToLuaMulti}; - -/// 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`. -/// -/// [`UserData`]: trait.UserData.html -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -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. - IDiv, - /// The bitwise AND (&) operator. - BAnd, - /// The bitwise OR (|) operator. - BOr, - /// The bitwise XOR (binary ~) operator. - BXor, - /// The bitwise NOT (unary ~) operator. - BNot, - /// The bitwise left shift (<<) operator. - Shl, - /// The bitwise right shift (>>) operator. - 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, -} - -pub(crate) fn meta_method_name(meta: MetaMethod) -> &'static str { - match meta { - MetaMethod::Add => "__add", - MetaMethod::Sub => "__sub", - MetaMethod::Mul => "__mul", - MetaMethod::Div => "__div", - MetaMethod::Mod => "__mod", - MetaMethod::Pow => "__pow", - MetaMethod::Unm => "__unm", - MetaMethod::IDiv => "__idiv", - MetaMethod::BAnd => "__band", - MetaMethod::BOr => "__bor", - MetaMethod::BXor => "__bxor", - MetaMethod::BNot => "__bnot", - MetaMethod::Shl => "__shl", - 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", - } -} - -/// Method registry for [`UserData`] implementors. -/// -/// [`UserData`]: trait.UserData.html -pub trait UserDataMethods<'lua, T: UserData> { - /// Add a 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(&mut self, name: &str, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result; - - /// 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`]: #method.add_method - fn add_method_mut(&mut self, name: &str, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result; - - /// Add a regular method as a function which accepts generic arguments, the first argument will - /// always be a `UserData` of type T. - /// - /// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use. - /// - /// [`add_method`]: #method.add_method - /// [`add_method_mut`]: #method.add_method_mut - fn add_function(&mut self, name: &str, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + Fn(&'lua Lua, A) -> Result; - - /// Add a regular method as a mutable function which accepts generic arguments, the first - /// argument will always be a `UserData` of type T. - /// - /// This is a version of [`add_function`] that accepts a FnMut argument. - /// - /// [`add_function`]: #method.add_function - fn add_function_mut(&mut self, name: &str, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + FnMut(&'lua Lua, A) -> Result; - - /// 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`]: #method.add_meta_function - fn add_meta_method(&mut self, meta: MetaMethod, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result; - - /// 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`]: #method.add_meta_function - fn add_meta_method_mut(&mut self, meta: MetaMethod, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result; - - /// 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(&mut self, meta: MetaMethod, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + Fn(&'lua Lua, A) -> Result; - - /// 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`]: #method.add_meta_function - fn add_meta_function_mut(&mut self, meta: MetaMethod, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + FnMut(&'lua Lua, A) -> Result; -} - -pub(crate) struct StaticUserDataMethods<'lua, T: 'static + UserData> { - pub(crate) methods: HashMap>, - pub(crate) meta_methods: HashMap>, - pub(crate) _type: PhantomData, -} - -impl<'lua, T: 'static + UserData> Default for StaticUserDataMethods<'lua, T> { - fn default() -> StaticUserDataMethods<'lua, T> { - StaticUserDataMethods { - methods: HashMap::new(), - meta_methods: HashMap::new(), - _type: PhantomData, - } - } -} - -impl<'lua, T: 'static + UserData> UserDataMethods<'lua, T> for StaticUserDataMethods<'lua, T> { - fn add_method(&mut self, name: &str, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, - { - self.methods - .insert(name.to_owned(), Self::box_method(method)); - } - - fn add_method_mut(&mut self, name: &str, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, - { - self.methods - .insert(name.to_owned(), Self::box_method_mut(method)); - } - - fn add_function(&mut self, name: &str, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + Fn(&'lua Lua, A) -> Result, - { - self.methods - .insert(name.to_owned(), Self::box_function(function)); - } - - fn add_function_mut(&mut self, name: &str, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + FnMut(&'lua Lua, A) -> Result, - { - self.methods - .insert(name.to_owned(), Self::box_function_mut(function)); - } - - fn add_meta_method(&mut self, meta: MetaMethod, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, - { - self.meta_methods.insert(meta, Self::box_method(method)); - } - - fn add_meta_method_mut(&mut self, meta: MetaMethod, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, - { - self.meta_methods.insert(meta, Self::box_method_mut(method)); - } - - fn add_meta_function(&mut self, meta: MetaMethod, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + Fn(&'lua Lua, A) -> Result, - { - self.meta_methods.insert(meta, Self::box_function(function)); - } - - fn add_meta_function_mut(&mut self, meta: MetaMethod, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + FnMut(&'lua Lua, A) -> Result, - { - self.meta_methods - .insert(meta, Self::box_function_mut(function)); - } -} - -impl<'lua, T: 'static + UserData> StaticUserDataMethods<'lua, T> { - fn box_method(method: M) -> Callback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, - { - Box::new(move |lua, mut args| { - if let Some(front) = args.pop_front() { - let userdata = AnyUserData::from_lua(front, lua)?; - let userdata = userdata.borrow::()?; - method(lua, &userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } else { - Err(Error::FromLuaConversionError { - from: "missing argument", - to: "userdata", - message: None, - }) - } - }) - } - - fn box_method_mut(method: M) -> Callback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, - { - let method = RefCell::new(method); - Box::new(move |lua, mut args| { - if let Some(front) = args.pop_front() { - let userdata = AnyUserData::from_lua(front, lua)?; - let mut userdata = userdata.borrow_mut::()?; - let mut method = method - .try_borrow_mut() - .map_err(|_| Error::RecursiveMutCallback)?; - (&mut *method)(lua, &mut userdata, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - } else { - Err(Error::FromLuaConversionError { - from: "missing argument", - to: "userdata", - message: None, - }) - } - }) - } - - fn box_function(function: F) -> Callback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + Fn(&'lua Lua, A) -> Result, - { - Box::new(move |lua, args| function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua)) - } - - fn box_function_mut(function: F) -> Callback<'lua, 'static> - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + FnMut(&'lua Lua, A) -> Result, - { - let function = RefCell::new(function); - Box::new(move |lua, args| { - let function = &mut *function - .try_borrow_mut() - .map_err(|_| Error::RecursiveMutCallback)?; - function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - }) - } -} - -pub(crate) enum NonStaticMethod<'lua, T> { - Method(Box) -> Result>>), - MethodMut(Box) -> Result>>), - Function(Box) -> Result>>), - FunctionMut(Box) -> Result>>), -} - -pub(crate) struct NonStaticUserDataMethods<'lua, T: UserData> { - pub(crate) methods: HashMap>, - pub(crate) meta_methods: HashMap>, -} - -impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> { - fn default() -> NonStaticUserDataMethods<'lua, T> { - NonStaticUserDataMethods { - methods: HashMap::new(), - meta_methods: HashMap::new(), - } - } -} - -impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> { - fn add_method(&mut self, name: &str, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, - { - self.methods.insert( - name.to_owned(), - NonStaticMethod::Method(Box::new(move |lua, ud, args| { - method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - })), - ); - } - - fn add_method_mut(&mut self, name: &str, mut method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, - { - self.methods.insert( - name.to_owned(), - NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| { - method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - })), - ); - } - - fn add_function(&mut self, name: &str, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + Fn(&'lua Lua, A) -> Result, - { - self.methods.insert( - name.to_owned(), - NonStaticMethod::Function(Box::new(move |lua, args| { - function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - })), - ); - } - - fn add_function_mut(&mut self, name: &str, mut function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + FnMut(&'lua Lua, A) -> Result, - { - self.methods.insert( - name.to_owned(), - NonStaticMethod::FunctionMut(Box::new(move |lua, args| { - function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - })), - ); - } - - fn add_meta_method(&mut self, meta: MetaMethod, method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, - { - self.meta_methods.insert( - meta, - NonStaticMethod::Method(Box::new(move |lua, ud, args| { - method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - })), - ); - } - - fn add_meta_method_mut(&mut self, meta: MetaMethod, mut method: M) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, - { - self.meta_methods.insert( - meta, - NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| { - method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - })), - ); - } - - fn add_meta_function(&mut self, meta: MetaMethod, function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + Fn(&'lua Lua, A) -> Result, - { - self.meta_methods.insert( - meta, - NonStaticMethod::Function(Box::new(move |lua, args| { - function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - })), - ); - } - - fn add_meta_function_mut(&mut self, meta: MetaMethod, mut function: F) - where - A: FromLuaMulti<'lua>, - R: ToLuaMulti<'lua>, - F: 'static + Send + FnMut(&'lua Lua, A) -> Result, - { - self.meta_methods.insert( - meta, - NonStaticMethod::FunctionMut(Box::new(move |lua, args| { - function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) - })), - ); - } -} diff --git a/src/scope.rs b/src/scope.rs index 3ef0f5f..5820c3e 100644 --- a/src/scope.rs +++ b/src/scope.rs @@ -1,17 +1,18 @@ use std::any::Any; use std::cell::RefCell; +use std::collections::HashMap; use std::marker::PhantomData; use std::mem; use std::os::raw::c_void; use std::rc::Rc; +use std::string::String as StdString; use error::{Error, Result}; use ffi; use function::Function; use lua::Lua; -use methods::{meta_method_name, NonStaticMethod, NonStaticUserDataMethods}; use types::Callback; -use userdata::{AnyUserData, UserData}; +use userdata::{AnyUserData, MetaMethod, UserData, UserDataMethods}; use util::{ assert_stack, init_userdata_metatable, protect_lua_closure, push_string, push_userdata, take_userdata, StackGuard, @@ -245,7 +246,7 @@ impl<'scope> Scope<'scope> { })?; for (k, m) in ud_methods.meta_methods { - push_string(lua.state, meta_method_name(k))?; + push_string(lua.state, k.name())?; lua.push_value(Value::Function(wrap_method(self, data.clone(), m)?)); protect_lua_closure(lua.state, 3, 1, |state| { @@ -323,3 +324,138 @@ impl<'scope> Drop for Scope<'scope> { drop(to_drop); } } + +enum NonStaticMethod<'lua, T> { + Method(Box) -> Result>>), + MethodMut(Box) -> Result>>), + Function(Box) -> Result>>), + FunctionMut(Box) -> Result>>), +} + +struct NonStaticUserDataMethods<'lua, T: UserData> { + methods: HashMap>, + meta_methods: HashMap>, +} + +impl<'lua, T: UserData> Default for NonStaticUserDataMethods<'lua, T> { + fn default() -> NonStaticUserDataMethods<'lua, T> { + NonStaticUserDataMethods { + methods: HashMap::new(), + meta_methods: HashMap::new(), + } + } +} + +impl<'lua, T: UserData> UserDataMethods<'lua, T> for NonStaticUserDataMethods<'lua, T> { + fn add_method(&mut self, name: &str, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, + { + self.methods.insert( + name.to_owned(), + NonStaticMethod::Method(Box::new(move |lua, ud, args| { + method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })), + ); + } + + fn add_method_mut(&mut self, name: &str, mut method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, + { + self.methods.insert( + name.to_owned(), + NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| { + method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })), + ); + } + + fn add_function(&mut self, name: &str, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + Fn(&'lua Lua, A) -> Result, + { + self.methods.insert( + name.to_owned(), + NonStaticMethod::Function(Box::new(move |lua, args| { + function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })), + ); + } + + fn add_function_mut(&mut self, name: &str, mut function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + FnMut(&'lua Lua, A) -> Result, + { + self.methods.insert( + name.to_owned(), + NonStaticMethod::FunctionMut(Box::new(move |lua, args| { + function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })), + ); + } + + fn add_meta_method(&mut self, meta: MetaMethod, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result, + { + self.meta_methods.insert( + meta, + NonStaticMethod::Method(Box::new(move |lua, ud, args| { + method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })), + ); + } + + fn add_meta_method_mut(&mut self, meta: MetaMethod, mut method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result, + { + self.meta_methods.insert( + meta, + NonStaticMethod::MethodMut(Box::new(move |lua, ud, args| { + method(lua, ud, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })), + ); + } + + fn add_meta_function(&mut self, meta: MetaMethod, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + Fn(&'lua Lua, A) -> Result, + { + self.meta_methods.insert( + meta, + NonStaticMethod::Function(Box::new(move |lua, args| { + function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })), + ); + } + + fn add_meta_function_mut(&mut self, meta: MetaMethod, mut function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + FnMut(&'lua Lua, A) -> Result, + { + self.meta_methods.insert( + meta, + NonStaticMethod::FunctionMut(Box::new(move |lua, args| { + function(lua, A::from_lua_multi(args, lua)?)?.to_lua_multi(lua) + })), + ); + } +} diff --git a/src/userdata.rs b/src/userdata.rs index d3d9eba..0a22e68 100644 --- a/src/userdata.rs +++ b/src/userdata.rs @@ -2,10 +2,202 @@ use std::cell::{Ref, RefCell, RefMut}; use error::{Error, Result}; use ffi; -use methods::UserDataMethods; +use lua::Lua; use types::LuaRef; use util::{assert_stack, get_userdata, StackGuard}; -use value::{FromLua, ToLua}; +use value::{FromLua, FromLuaMulti, ToLua, ToLuaMulti}; + +/// 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`. +/// +/// [`UserData`]: trait.UserData.html +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +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. + IDiv, + /// The bitwise AND (&) operator. + BAnd, + /// The bitwise OR (|) operator. + BOr, + /// The bitwise XOR (binary ~) operator. + BXor, + /// The bitwise NOT (unary ~) operator. + BNot, + /// The bitwise left shift (<<) operator. + Shl, + /// The bitwise right shift (>>) operator. + 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, +} + +impl MetaMethod { + pub(crate) 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", + MetaMethod::IDiv => "__idiv", + MetaMethod::BAnd => "__band", + MetaMethod::BOr => "__bor", + MetaMethod::BXor => "__bxor", + MetaMethod::BNot => "__bnot", + MetaMethod::Shl => "__shl", + 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", + } + } +} + +/// Method registry for [`UserData`] implementors. +/// +/// [`UserData`]: trait.UserData.html +pub trait UserDataMethods<'lua, T: UserData> { + /// Add a 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(&mut self, name: &str, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result; + + /// 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`]: #method.add_method + fn add_method_mut(&mut self, name: &str, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result; + + /// Add a regular method as a function which accepts generic arguments, the first argument will + /// always be a `UserData` of type T. + /// + /// Prefer to use [`add_method`] or [`add_method_mut`] as they are easier to use. + /// + /// [`add_method`]: #method.add_method + /// [`add_method_mut`]: #method.add_method_mut + fn add_function(&mut self, name: &str, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + Fn(&'lua Lua, A) -> Result; + + /// Add a regular method as a mutable function which accepts generic arguments, the first + /// argument will always be a `UserData` of type T. + /// + /// This is a version of [`add_function`] that accepts a FnMut argument. + /// + /// [`add_function`]: #method.add_function + fn add_function_mut(&mut self, name: &str, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + FnMut(&'lua Lua, A) -> Result; + + /// 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`]: #method.add_meta_function + fn add_meta_method(&mut self, meta: MetaMethod, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + Fn(&'lua Lua, &T, A) -> Result; + + /// 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`]: #method.add_meta_function + fn add_meta_method_mut(&mut self, meta: MetaMethod, method: M) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + M: 'static + Send + FnMut(&'lua Lua, &mut T, A) -> Result; + + /// 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(&mut self, meta: MetaMethod, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + Fn(&'lua Lua, A) -> Result; + + /// 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`]: #method.add_meta_function + fn add_meta_function_mut(&mut self, meta: MetaMethod, function: F) + where + A: FromLuaMulti<'lua>, + R: ToLuaMulti<'lua>, + F: 'static + Send + FnMut(&'lua Lua, A) -> Result; +} /// Trait for custom userdata types. ///