use core::any::type_name;
use super::{AnyUserdata, UserdataRegistry};
use crate::callback::{Arguments, CallbackReturn};
use crate::error::Error;
use crate::lua::LuaRef;
use crate::value::{IntoLua, Value};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum MetaMethod {
Add,
Sub,
Mul,
Div,
IDiv,
Mod,
Pow,
Unm,
Eq,
Lt,
Le,
Len,
Concat,
Index,
NewIndex,
Call,
ToString,
ToDebugString,
NameCall,
Iter,
Type,
}
impl MetaMethod {
pub const fn name(self) -> &'static str {
match self {
Self::Add => "__add",
Self::Sub => "__sub",
Self::Mul => "__mul",
Self::Div => "__div",
Self::IDiv => "__idiv",
Self::Mod => "__mod",
Self::Pow => "__pow",
Self::Unm => "__unm",
Self::Eq => "__eq",
Self::Lt => "__lt",
Self::Le => "__le",
Self::Len => "__len",
Self::Concat => "__concat",
Self::Index => "__index",
Self::NewIndex => "__newindex",
Self::Call => "__call",
Self::ToString => "__tostring",
Self::ToDebugString => "__todebugstring",
Self::NameCall => "__namecall",
Self::Iter => "__iter",
Self::Type => "__type",
}
}
pub(crate) fn validate(name: &[u8]) -> Result<(), Error> {
if matches!(name, b"__gc" | b"__metatable") {
return Err(Error::MetaMethodRestricted(
String::from_utf8_lossy(name).into_owned(),
));
}
Ok(())
}
}
impl AsRef<str> for MetaMethod {
fn as_ref(&self) -> &str {
self.name()
}
}
impl AsRef<[u8]> for MetaMethod {
fn as_ref(&self) -> &[u8] {
self.name().as_bytes()
}
}
pub trait Userdata: Sized {
fn add_fields<F: UserdataFields<Self>>(_fields: &mut F) {}
fn add_methods<M: UserdataMethods<Self>>(_methods: &mut M) {}
fn register(registry: &mut UserdataRegistry<'_, Self>)
where
Self: 'static,
{
Self::add_fields(registry);
Self::add_methods(registry);
#[cfg(feature = "macros")]
super::register_userdata_impls(registry);
}
}
pub trait UserdataFields<T> {
fn add_field<V>(&mut self, name: impl AsRef<[u8]>, value: V)
where
V: for<'lua> IntoLua<'lua>;
fn add_field_with<F>(&mut self, name: impl AsRef<[u8]>, field: F)
where
F: for<'lua> FnOnce(LuaRef<'lua>) -> Result<Value<'lua>, Error> + 'static;
fn add_field_method_get<M>(&mut self, name: impl AsRef<[u8]>, method: M)
where
M: for<'call> Fn(
LuaRef<'call>,
&T,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_field_method_set<M>(&mut self, name: impl AsRef<[u8]>, method: M)
where
M: for<'call> FnMut(
LuaRef<'call>,
&mut T,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_field_function_get<F>(&mut self, name: impl AsRef<[u8]>, function: F)
where
F: for<'call> Fn(LuaRef<'call>, Arguments<'call>) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_field_function_set<F>(&mut self, name: impl AsRef<[u8]>, function: F)
where
F: for<'call> FnMut(
LuaRef<'call>,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_meta_field<V>(&mut self, name: impl AsRef<[u8]>, value: V)
where
V: for<'lua> IntoLua<'lua>;
fn add_meta_field_with<F>(&mut self, name: impl AsRef<[u8]>, field: F)
where
F: for<'lua> FnOnce(LuaRef<'lua>) -> Result<Value<'lua>, Error> + 'static;
}
pub trait UserdataMethods<T> {
fn add_method<M>(&mut self, name: impl AsRef<[u8]>, method: M)
where
M: for<'call> Fn(
LuaRef<'call>,
&T,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_method_mut<M>(&mut self, name: impl AsRef<[u8]>, method: M)
where
M: for<'call> FnMut(
LuaRef<'call>,
&mut T,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_method_once<M>(&mut self, name: impl AsRef<[u8]>, method: M)
where
T: 'static,
M: for<'call> Fn(
LuaRef<'call>,
T,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static,
{
self.add_function(name, move |lua, mut arguments| {
let userdata: AnyUserdata<'_> = arguments.next()?;
let value = userdata
.take::<T>()
.map_err(|error| Error::bad_argument(1, error))?;
method(lua, value, arguments)
});
}
fn add_function<F>(&mut self, name: impl AsRef<[u8]>, function: F)
where
F: for<'call> Fn(LuaRef<'call>, Arguments<'call>) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_function_mut<F>(&mut self, name: impl AsRef<[u8]>, function: F)
where
F: for<'call> FnMut(
LuaRef<'call>,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_meta_method<M>(&mut self, name: impl AsRef<[u8]>, method: M)
where
M: for<'call> Fn(
LuaRef<'call>,
&T,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_meta_method_mut<M>(&mut self, name: impl AsRef<[u8]>, method: M)
where
M: for<'call> FnMut(
LuaRef<'call>,
&mut T,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_meta_function<F>(&mut self, name: impl AsRef<[u8]>, function: F)
where
F: for<'call> Fn(LuaRef<'call>, Arguments<'call>) -> Result<CallbackReturn<'call>, Error>
+ 'static;
fn add_meta_function_mut<F>(&mut self, name: impl AsRef<[u8]>, function: F)
where
F: for<'call> FnMut(
LuaRef<'call>,
Arguments<'call>,
) -> Result<CallbackReturn<'call>, Error>
+ 'static;
}
pub(super) fn short_type_name<T: ?Sized>() -> String {
let full_name = type_name::<T>();
let mut name = String::new();
let mut index = 0;
while index < full_name.len() {
let remaining = &full_name[index..];
if let Some(special_index) =
remaining.find(|c: char| [' ', '<', '>', '(', ')', '[', ']', ',', ';'].contains(&c))
{
name.push_str(collapse_type_name(&remaining[..special_index]));
name.push_str(&remaining[special_index..=special_index]);
if name.ends_with("<'_>") || name.ends_with("<'_, ") {
name.truncate(name.len() - 4);
}
let after_special = special_index + 1;
if matches!(&remaining[special_index..=special_index], ">" | ")" | "]")
&& remaining[after_special..].starts_with("::")
{
name.push_str("::");
index += after_special + 2;
} else {
index += after_special;
}
} else {
name.push_str(collapse_type_name(remaining));
break;
}
}
name
}
fn collapse_type_name(segment: &str) -> &str {
segment.rsplit("::").next().unwrap_or(segment)
}