feat: add conversions from tuples of TryFrom<Object> to Array/Object (#236)

This commit is contained in:
Riccardo Mazzarini 2025-05-22 19:20:26 +02:00 committed by GitHub
parent 80caa4cda4
commit bac13dfcb1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 176 additions and 39 deletions

View file

@ -11,6 +11,28 @@ use crate::Object;
#[repr(transparent)]
pub struct Array(pub(super) KVec<Object>);
/// An owning iterator over the `Object`s of a [`Array`].
#[derive(Clone)]
pub struct ArrayIterator(kvec::IntoIter<Object>);
/// The error type returned when trying to convert an [`Array`] into a tuple.
#[derive(Debug, PartialEq, Eq, thiserror::Error)]
pub enum ArrayFromTupleError<T> {
/// Not enough elements in the array.
#[error(
"not enough elements in the array, expected {expected_len} but got \
{actual_len}"
)]
NotEnoughElements { expected_len: usize, actual_len: usize },
/// The tuple element at the given index couldn't be converted into the
/// requested type.
#[error(
"couldn't convert tuple element at index {element_idx} into object: \
{error:?}"
)]
ElementFromObject { element_idx: usize, error: T },
}
impl core::fmt::Debug for Array {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
@ -120,10 +142,6 @@ impl IntoIterator for Array {
}
}
/// An owning iterator over the `Object`s of a [`Array`].
#[derive(Clone)]
pub struct ArrayIterator(kvec::IntoIter<Object>);
impl Iterator for ArrayIterator {
type Item = Object;
@ -207,7 +225,7 @@ impl lua::Pushable for Array {
/// Implements `From<(A, B, C, ..)>` for tuples `(A, B, C, ..)` where all the
/// elements in the tuple are `Into<Object>`.
macro_rules! from_tuple {
macro_rules! array_from_tuple {
($($ty:ident)*) => {
impl <$($ty: Into<Object>),*> From<($($ty,)*)> for Array {
#[allow(non_snake_case)]
@ -218,22 +236,84 @@ macro_rules! from_tuple {
};
}
from_tuple!(A);
from_tuple!(A B);
from_tuple!(A B C);
from_tuple!(A B C D);
from_tuple!(A B C D E);
from_tuple!(A B C D E F);
from_tuple!(A B C D E F G);
from_tuple!(A B C D E F G H);
from_tuple!(A B C D E F G H I);
from_tuple!(A B C D E F G H I J);
from_tuple!(A B C D E F G H I J K);
from_tuple!(A B C D E F G H I J K L);
from_tuple!(A B C D E F G H I J K L M);
from_tuple!(A B C D E F G H I J K L M N);
from_tuple!(A B C D E F G H I J K L M N O);
from_tuple!(A B C D E F G H I J K L M N O P);
array_from_tuple!(A);
array_from_tuple!(A B);
array_from_tuple!(A B C);
array_from_tuple!(A B C D);
array_from_tuple!(A B C D E);
array_from_tuple!(A B C D E F);
array_from_tuple!(A B C D E F G);
array_from_tuple!(A B C D E F G H);
array_from_tuple!(A B C D E F G H I);
array_from_tuple!(A B C D E F G H I J);
array_from_tuple!(A B C D E F G H I J K);
array_from_tuple!(A B C D E F G H I J K L);
array_from_tuple!(A B C D E F G H I J K L M);
array_from_tuple!(A B C D E F G H I J K L M N);
array_from_tuple!(A B C D E F G H I J K L M N O);
array_from_tuple!(A B C D E F G H I J K L M N O P);
macro_rules! count {
() => {0i32};
($x:tt $($xs:tt)*) => {1i32 + count!($($xs)*)};
}
/// Implements `TryFrom<Array>` for tuples `(A, B, C, ..)` where all the
/// elements in the tuple are `TryFrom<Object>` with the same error.
macro_rules! tuple_try_from_array {
($($ty:ident)*) => {
impl<Error, $($ty,)*> TryFrom<Array> for ($($ty,)*)
where
$($ty: TryFrom<Object, Error = Error>,)*
{
type Error = ArrayFromTupleError<Error>;
#[inline]
#[allow(non_snake_case)]
fn try_from(array: Array) -> Result<Self, Self::Error> {
let actual_len = array.len();
let expected_len = count!($($ty)*) as usize;
if actual_len < expected_len {
return Err(ArrayFromTupleError::NotEnoughElements {
expected_len,
actual_len
});
}
let mut iter = array.into_iter();
$(
let $ty = $ty::try_from(
iter.next().expect("already checked len")
).map_err(|error| ArrayFromTupleError::ElementFromObject {
element_idx: actual_len - iter.len() - 1,
error
})?;
)*
Ok(($($ty,)*))
}
}
};
}
tuple_try_from_array!(A);
tuple_try_from_array!(A B);
tuple_try_from_array!(A B C);
tuple_try_from_array!(A B C D);
tuple_try_from_array!(A B C D E);
tuple_try_from_array!(A B C D E F);
tuple_try_from_array!(A B C D E F G);
tuple_try_from_array!(A B C D E F G H);
tuple_try_from_array!(A B C D E F G H I);
tuple_try_from_array!(A B C D E F G H I J);
tuple_try_from_array!(A B C D E F G H I J K);
tuple_try_from_array!(A B C D E F G H I J K L);
tuple_try_from_array!(A B C D E F G H I J K L M);
tuple_try_from_array!(A B C D E F G H I J K L M N);
tuple_try_from_array!(A B C D E F G H I J K L M N O);
tuple_try_from_array!(A B C D E F G H I J K L M N O P);
#[cfg(test)]
mod tests {

View file

@ -4,6 +4,7 @@ use std::collections::HashMap;
use thiserror::Error as ThisError;
use crate::array::ArrayFromTupleError;
use crate::{
Array,
Boolean,
@ -33,6 +34,10 @@ pub enum Error {
#[cfg(feature = "serde")]
#[error(transparent)]
Serialize(#[from] crate::serde::SerializeError),
#[doc(hidden)]
#[error("{0}")]
Other(String),
}
/// Trait implemented for types can be obtained from an [`Object`].
@ -77,8 +82,10 @@ impl FromObject for Boolean {
}
}
impl FromObject for Integer {
fn from_object(obj: Object) -> Result<Self, Error> {
impl TryFrom<Object> for Integer {
type Error = Error;
fn try_from(obj: Object) -> Result<Self, Self::Error> {
match obj.kind() {
ObjectKind::Integer
| ObjectKind::Buffer
@ -162,6 +169,13 @@ impl<A, R> FromObject for Function<A, R> {
}
}
impl<T: TryFrom<Object, Error = Error>> FromObject for T {
#[inline]
fn from_object(obj: Object) -> Result<Self, Error> {
T::try_from(obj)
}
}
/// Implements `FromObject` for a type that implements `From<Integer>`.
macro_rules! from_int {
($integer:ty) => {
@ -175,27 +189,30 @@ macro_rules! from_int {
from_int!(i128);
/// Implements `FromObject` for a type that implements `TryFrom<Integer>`.
macro_rules! try_from_int {
/// Implements `TryFrom<Object>` for a type that implements `TryFrom<Integer>`.
macro_rules! int_try_from_obj {
($integer:ty) => {
impl FromObject for $integer {
fn from_object(obj: Object) -> Result<Self, Error> {
Integer::from_object(obj).and_then(|n| Ok(n.try_into()?))
impl TryFrom<Object> for $integer {
type Error = Error;
fn try_from(obj: Object) -> Result<Self, Self::Error> {
Integer::try_from(obj)
.and_then(|n| n.try_into().map_err(Into::into))
}
}
};
}
try_from_int!(i8);
try_from_int!(u8);
try_from_int!(i16);
try_from_int!(u16);
try_from_int!(i32);
try_from_int!(u32);
try_from_int!(u64);
try_from_int!(u128);
try_from_int!(isize);
try_from_int!(usize);
int_try_from_obj!(i8);
int_try_from_obj!(u8);
int_try_from_obj!(i16);
int_try_from_obj!(u16);
int_try_from_obj!(i32);
int_try_from_obj!(u32);
int_try_from_obj!(u64);
int_try_from_obj!(u128);
int_try_from_obj!(isize);
int_try_from_obj!(usize);
impl FromObject for f32 {
fn from_object(obj: Object) -> Result<Self, Error> {
@ -231,6 +248,46 @@ where
}
}
/// Implements `FromObject` for tuples `(A, B, C, ..)` where all the
/// elements in the tuple are `TryFrom<Object>` with the same error.
macro_rules! tuple_from_object {
($($ty:ident)*) => {
impl<Err, $($ty,)*> FromObject for ($($ty,)*)
where
$($ty: TryFrom<Object, Error = Err>,)*
Err: Into<self::Error> + core::error::Error,
{
#[inline]
#[allow(non_snake_case)]
fn from_object(obj: Object) -> Result<Self, Error> {
Array::from_object(obj)?
.try_into()
.map_err(|err: ArrayFromTupleError<Err>| match err {
ArrayFromTupleError::ElementFromObject { error, .. } => error.into(),
err @ ArrayFromTupleError::NotEnoughElements { .. } => Error::Other(err.to_string()),
})
}
}
};
}
tuple_from_object!(A);
tuple_from_object!(A B);
tuple_from_object!(A B C);
tuple_from_object!(A B C D);
tuple_from_object!(A B C D E);
tuple_from_object!(A B C D E F);
tuple_from_object!(A B C D E F G);
tuple_from_object!(A B C D E F G H);
tuple_from_object!(A B C D E F G H I);
tuple_from_object!(A B C D E F G H I J);
tuple_from_object!(A B C D E F G H I J K);
tuple_from_object!(A B C D E F G H I J K L);
tuple_from_object!(A B C D E F G H I J K L M);
tuple_from_object!(A B C D E F G H I J K L M N);
tuple_from_object!(A B C D E F G H I J K L M N O);
tuple_from_object!(A B C D E F G H I J K L M N O P);
impl<T> ToObject for T
where
T: Into<Object>,

View file

@ -17,7 +17,7 @@ pub mod serde;
mod string;
pub use arena::{arena, arena_init, Arena};
pub use array::Array;
pub use array::{Array, ArrayFromTupleError};
pub use dictionary::{Dictionary, KeyValuePair};
pub use error::Error;
pub use function::Function;