use oxi_luajit as lua;
use crate::kvec::{self, KVec};
use crate::NonOwning;
use crate::Object;
#[derive(Clone, Default, PartialEq)]
#[repr(transparent)]
pub struct Array(pub(super) KVec<Object>);
impl core::fmt::Debug for Array {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
f.debug_list().entries(self.iter()).finish()
}
}
impl Array {
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
#[inline]
pub fn iter(&self) -> core::slice::Iter<'_, Object> {
self.0.as_slice().iter()
}
#[inline]
pub fn new() -> Self {
Self(KVec::new())
}
#[inline]
pub fn non_owning(&self) -> NonOwning<'_, Self> {
#[allow(clippy::unnecessary_struct_initialization)]
NonOwning::new(Self(KVec { ..self.0 }))
}
}
impl<T: Into<Object>> FromIterator<T> for Array {
#[inline]
fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
Self(
iter.into_iter()
.map(Into::into)
.filter(|obj| obj.is_some())
.collect(),
)
}
}
impl IntoIterator for Array {
type Item = Object;
type IntoIter = ArrayIterator;
#[inline]
fn into_iter(self) -> Self::IntoIter {
ArrayIterator(self.0.into_iter())
}
}
#[derive(Clone)]
pub struct ArrayIterator(kvec::IntoIter<Object>);
impl Iterator for ArrayIterator {
type Item = Object;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl ExactSizeIterator for ArrayIterator {
#[inline]
fn len(&self) -> usize {
self.0.len()
}
}
impl DoubleEndedIterator for ArrayIterator {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back()
}
}
impl core::iter::FusedIterator for ArrayIterator {}
impl lua::Poppable for Array {
#[inline]
unsafe fn pop(
lstate: *mut lua::ffi::lua_State,
) -> Result<Self, lua::Error> {
use lua::ffi::*;
if lua_gettop(lstate) == 0 {
return Err(lua::Error::PopEmptyStack);
} else if lua_type(lstate, -1) != LUA_TTABLE {
let ty = lua_type(lstate, -1);
return Err(lua::Error::pop_wrong_type::<Self>(LUA_TTABLE, ty));
}
let mut kvec = KVec::with_capacity(lua_objlen(lstate, -1));
lua_pushnil(lstate);
while lua_next(lstate, -2) != 0 {
kvec.push(Object::pop(lstate)?);
}
lua_pop(lstate, 1);
Ok(Self(kvec))
}
}
impl lua::Pushable for Array {
#[inline]
unsafe fn push(
self,
lstate: *mut lua::ffi::lua_State,
) -> Result<core::ffi::c_int, lua::Error> {
use lua::ffi::*;
lua_createtable(lstate, self.len() as _, 0);
for (idx, obj) in self.into_iter().enumerate() {
obj.push(lstate)?;
lua_rawseti(lstate, -2, (idx + 1) as _);
}
Ok(1)
}
}
macro_rules! from_tuple {
($($ty:ident)*) => {
impl <$($ty: Into<Object>),*> From<($($ty,)*)> for Array {
#[allow(non_snake_case)]
fn from(($($ty,)*): ($($ty,)*)) -> Self {
Self::from_iter([$($ty.into(),)*])
}
}
};
}
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);
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn array_layout() {
use core::alloc::Layout;
assert_eq!(Layout::new::<Array>(), Layout::new::<KVec<Object>>());
}
#[test]
fn iter_basic() {
let array = Array::from_iter(["Foo", "Bar", "Baz"]);
let mut iter = array.into_iter();
assert_eq!(Some(Object::from("Foo")), iter.next());
assert_eq!(Some(Object::from("Bar")), iter.next());
assert_eq!(Some(Object::from("Baz")), iter.next());
assert_eq!(None, iter.next());
}
#[test]
fn drop_iter_halfway() {
let array = Array::from_iter(["Foo", "Bar", "Baz"]);
let mut iter = array.into_iter();
assert_eq!(Some(Object::from("Foo")), iter.next());
}
#[test]
fn empty_array() {
let empty = Array::default();
assert_eq!(0, empty.into_iter().count());
}
}