Skip to content

Instantly share code, notes, and snippets.

@Caellian
Created February 7, 2024 04:19
Show Gist options
  • Save Caellian/a22334437958fae2a60176ec0efd45f3 to your computer and use it in GitHub Desktop.
Save Caellian/a22334437958fae2a60176ec0efd45f3 to your computer and use it in GitHub Desktop.
/// Argument that's allowed to fail conversion and will be skipped, yielding `None`.
pub struct LuaFallible<T>(Option<T>);
impl<T> Deref for LuaFallible<T> {
type Target = Option<T>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
pub struct ConversionSuccess<T: Sized> {
value: T,
consumed_arguments: usize,
}
pub type ConversionResult<T> = std::result::Result<ConversionSuccess<T>, mlua::Error>;
pub trait FromArgPack<'lua>: Sized {
fn convert(
args: &mut Vec<Value<'lua>>,
lua: &'lua Lua,
position: usize,
) -> ConversionResult<Self>;
}
impl<'lua, T: FromLua<'lua>> FromArgPack<'lua> for T {
fn convert(
args: &mut Vec<Value<'lua>>,
lua: &'lua Lua,
position: usize,
) -> ConversionResult<Self> {
let (arg, was_none) = match args.pop() {
Some(it) => (it, false),
None => (Nil, true),
};
match Self::from_lua(arg, lua) {
Ok(it) => Ok(ConversionSuccess {
value: it,
consumed_arguments: 1,
}),
Err(err) => {
if !was_none {
args.push(arg);
}
Err(Error::BadArgument {
to: None,
pos: position,
name: None,
cause: Arc::new(err),
})
}
}
}
}
impl<'lua, T: FromLua<'lua>> FromArgPack<'lua> for LuaFallible<T> {
fn convert(
args: &mut Vec<Value<'lua>>,
lua: &'lua Lua,
position: usize,
) -> ConversionResult<LuaFallible<T>> {
let (arg, was_none) = match args.pop() {
Some(it) => (it, false),
None => (Nil, true),
};
Ok(match T::from_lua(arg, lua) {
Ok(it) => ConversionSuccess {
value: LuaFallible(Some(it)),
consumed_arguments: 1,
},
Err(_) => {
if !was_none {
args.push(arg);
}
ConversionSuccess {
value: LuaFallible(None),
consumed_arguments: 0,
}
}
})
}
}
// TODO: Compose with FromArgPack
/*
impl<'lua, T: FromLua<'lua>> FromArgPack<'lua> for Variadic<T> {
fn convert(args: &mut Vec<Value<'lua>>, lua: &'lua Lua) -> ConversionResult<Self> {
let consumed_arguments = args.len();
let value = Vec::with_capacity(consumed_arguments);
for el in args.drain(..).rev() {
value.push(T::from_lua(el, lua)?)
}
Ok(ConversionSuccess {
value,
consumed_arguments,
})
}
} */
pub trait FromArgs<'lua>: Sized {
fn from_arguments(args: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self>;
}
macro_rules! smaller_tuples_too {
($m: ident, $ty: ident) => {
$m!{}
$m!{$ty}
};
($m: ident, $ty: ident, $($tt: ident),*) => {
smaller_tuples_too!{$m, $($tt),*}
$m!{$ty, $($tt),*}
};
}
macro_rules! from_args_impl {
($($A:ident),*) => {
impl<'lua$(,$A)*> FromArgs<'lua> for ($($A,)*)
where
$(
$A: FromArgPack<'lua>,
)*
{
#[allow(non_snake_case)]
fn from_arguments(args: MultiValue<'lua>, lua: &'lua Lua) -> Result<Self> {
let mut args = args.into_vec();
args.reverse();
let mut argc = args.len();
let mut at = 0;
$(
let $A = $A::convert(&mut args, lua, at)?;
at += $A.consumed_arguments;
let $A = $A.value;
)*
if at < argc {
return Err(mlua::Error::runtime(format!(
"too many arguments; got: {}, expected: {}",
argc,
at - 1
)));
}
return Ok(($($A,)*));
}
}
};
}
smaller_tuples_too!(from_args_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
pub trait UserDataMethodsExt<'lua, T>: UserDataMethods<'lua, T> {
fn add_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + 'static,
A: FromArgs<'lua>,
R: IntoLuaMulti<'lua>,
{
UserDataMethods::add_method(self, name, |lua, t, args| {
method(lua, t, A::from_arguments(args, lua)?)
})
}
fn add_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + 'static,
A: FromArgs<'lua>,
R: IntoLuaMulti<'lua>,
{
UserDataMethods::add_method_mut(self, name, |lua, t, args| {
method(lua, t, A::from_arguments(args, lua)?)
})
}
fn add_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + 'static,
A: FromArgs<'lua>,
R: IntoLuaMulti<'lua>,
{
UserDataMethods::add_function(self, name, |lua, args| {
function(lua, A::from_arguments(args, lua)?)
})
}
fn add_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: FnMut(&'lua Lua, A) -> Result<R> + 'static,
A: FromArgs<'lua>,
R: IntoLuaMulti<'lua>,
{
UserDataMethods::add_function_mut(self, name, |lua, args| {
function(lua, A::from_arguments(args, lua)?)
})
}
fn add_meta_method<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: Fn(&'lua Lua, &T, A) -> Result<R> + 'static,
A: FromArgs<'lua>,
R: IntoLuaMulti<'lua>,
{
UserDataMethods::add_meta_method(self, name, |lua, t, args| {
method(lua, t, A::from_arguments(args, lua)?)
})
}
fn add_meta_method_mut<M, A, R>(&mut self, name: impl AsRef<str>, method: M)
where
M: FnMut(&'lua Lua, &mut T, A) -> Result<R> + 'static,
A: FromArgs<'lua>,
R: IntoLuaMulti<'lua>,
{
UserDataMethods::add_meta_method_mut(self, name, |lua, t, args| {
method(lua, t, A::from_arguments(args, lua)?)
})
}
fn add_meta_function<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: Fn(&'lua Lua, A) -> Result<R> + 'static,
A: FromArgs<'lua>,
R: IntoLuaMulti<'lua>,
{
UserDataMethods::add_meta_function(self, name, |lua, args| {
function(lua, A::from_arguments(args, lua)?)
})
}
fn add_meta_function_mut<F, A, R>(&mut self, name: impl AsRef<str>, function: F)
where
F: FnMut(&'lua Lua, A) -> Result<R> + 'static,
A: FromArgs<'lua>,
R: IntoLuaMulti<'lua>,
{
UserDataMethods::add_meta_function_mut(self, name, |lua, args| {
function(lua, A::from_arguments(args, lua)?)
})
}
}
impl<'lua, T, M: UserDataMethods<'lua, T>> UserDataMethodsExt<'lua, T> for M {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment