oxi_types/
array.rs

1use oxi_luajit as lua;
2
3use crate::kvec::{self, KVec};
4use crate::NonOwning;
5use crate::Object;
6
7/// A vector of Neovim [`Object`]s.
8#[derive(Clone, Default, PartialEq)]
9#[repr(transparent)]
10pub struct Array(pub(super) KVec<Object>);
11
12impl core::fmt::Debug for Array {
13    #[inline]
14    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
15        f.debug_list().entries(self.iter()).finish()
16    }
17}
18
19impl Array {
20    /// Returns the number of elements in the array.
21    #[inline]
22    pub fn len(&self) -> usize {
23        self.0.len()
24    }
25
26    /// Returns `true` if the array contains no elements.
27    #[inline]
28    pub fn is_empty(&self) -> bool {
29        self.0.is_empty()
30    }
31
32    /// Returns an iterator over the `Object`s of the array.
33    #[inline]
34    pub fn iter(&self) -> core::slice::Iter<'_, Object> {
35        self.0.as_slice().iter()
36    }
37
38    /// Creates a new, empty `Array`.
39    #[inline]
40    pub fn new() -> Self {
41        Self(KVec::new())
42    }
43
44    /// Returns a non-owning version of this `Array`.
45    #[inline]
46    pub fn non_owning(&self) -> NonOwning<'_, Self> {
47        #[allow(clippy::unnecessary_struct_initialization)]
48        NonOwning::new(Self(KVec { ..self.0 }))
49    }
50}
51
52impl<T: Into<Object>> FromIterator<T> for Array {
53    #[inline]
54    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
55        Self(
56            iter.into_iter()
57                .map(Into::into)
58                .filter(|obj| obj.is_some())
59                .collect(),
60        )
61    }
62}
63
64impl IntoIterator for Array {
65    type Item = Object;
66    type IntoIter = ArrayIterator;
67
68    #[inline]
69    fn into_iter(self) -> Self::IntoIter {
70        ArrayIterator(self.0.into_iter())
71    }
72}
73
74/// An owning iterator over the `Object`s of a [`Array`].
75#[derive(Clone)]
76pub struct ArrayIterator(kvec::IntoIter<Object>);
77
78impl Iterator for ArrayIterator {
79    type Item = Object;
80
81    #[inline]
82    fn next(&mut self) -> Option<Self::Item> {
83        self.0.next()
84    }
85
86    #[inline]
87    fn size_hint(&self) -> (usize, Option<usize>) {
88        self.0.size_hint()
89    }
90}
91
92impl ExactSizeIterator for ArrayIterator {
93    #[inline]
94    fn len(&self) -> usize {
95        self.0.len()
96    }
97}
98
99impl DoubleEndedIterator for ArrayIterator {
100    #[inline]
101    fn next_back(&mut self) -> Option<Self::Item> {
102        self.0.next_back()
103    }
104}
105
106impl core::iter::FusedIterator for ArrayIterator {}
107
108impl lua::Poppable for Array {
109    #[inline]
110    unsafe fn pop(
111        lstate: *mut lua::ffi::lua_State,
112    ) -> Result<Self, lua::Error> {
113        use lua::ffi::*;
114
115        if lua_gettop(lstate) == 0 {
116            return Err(lua::Error::PopEmptyStack);
117        } else if lua_type(lstate, -1) != LUA_TTABLE {
118            let ty = lua_type(lstate, -1);
119            return Err(lua::Error::pop_wrong_type::<Self>(LUA_TTABLE, ty));
120        }
121
122        // TODO: check that the table is an array-like table and not a
123        // dictionary-like one?
124
125        let mut kvec = KVec::with_capacity(lua_objlen(lstate, -1));
126
127        lua_pushnil(lstate);
128
129        while lua_next(lstate, -2) != 0 {
130            kvec.push(Object::pop(lstate)?);
131        }
132
133        // Pop the table.
134        lua_pop(lstate, 1);
135
136        Ok(Self(kvec))
137    }
138}
139
140impl lua::Pushable for Array {
141    #[inline]
142    unsafe fn push(
143        self,
144        lstate: *mut lua::ffi::lua_State,
145    ) -> Result<core::ffi::c_int, lua::Error> {
146        use lua::ffi::*;
147
148        lua_createtable(lstate, self.len() as _, 0);
149
150        for (idx, obj) in self.into_iter().enumerate() {
151            obj.push(lstate)?;
152            lua_rawseti(lstate, -2, (idx + 1) as _);
153        }
154
155        Ok(1)
156    }
157}
158
159/// Implements `From<(A, B, C, ..)>` for tuples `(A, B, C, ..)` where all the
160/// elements in the tuple are `Into<Object>`.
161macro_rules! from_tuple {
162    ($($ty:ident)*) => {
163        impl <$($ty: Into<Object>),*> From<($($ty,)*)> for Array {
164            #[allow(non_snake_case)]
165            fn from(($($ty,)*): ($($ty,)*)) -> Self {
166                Self::from_iter([$($ty.into(),)*])
167            }
168        }
169    };
170}
171
172from_tuple!(A);
173from_tuple!(A B);
174from_tuple!(A B C);
175from_tuple!(A B C D);
176from_tuple!(A B C D E);
177from_tuple!(A B C D E F);
178from_tuple!(A B C D E F G);
179from_tuple!(A B C D E F G H);
180from_tuple!(A B C D E F G H I);
181from_tuple!(A B C D E F G H I J);
182from_tuple!(A B C D E F G H I J K);
183from_tuple!(A B C D E F G H I J K L);
184from_tuple!(A B C D E F G H I J K L M);
185from_tuple!(A B C D E F G H I J K L M N);
186from_tuple!(A B C D E F G H I J K L M N O);
187from_tuple!(A B C D E F G H I J K L M N O P);
188
189#[cfg(test)]
190mod tests {
191    use super::*;
192
193    #[test]
194    fn array_layout() {
195        use core::alloc::Layout;
196
197        assert_eq!(Layout::new::<Array>(), Layout::new::<KVec<Object>>());
198    }
199
200    #[test]
201    fn iter_basic() {
202        let array = Array::from_iter(["Foo", "Bar", "Baz"]);
203
204        let mut iter = array.into_iter();
205        assert_eq!(Some(Object::from("Foo")), iter.next());
206        assert_eq!(Some(Object::from("Bar")), iter.next());
207        assert_eq!(Some(Object::from("Baz")), iter.next());
208        assert_eq!(None, iter.next());
209    }
210
211    #[test]
212    fn drop_iter_halfway() {
213        let array = Array::from_iter(["Foo", "Bar", "Baz"]);
214
215        let mut iter = array.into_iter();
216        assert_eq!(Some(Object::from("Foo")), iter.next());
217    }
218
219    #[test]
220    fn empty_array() {
221        let empty = Array::default();
222        assert_eq!(0, empty.into_iter().count());
223    }
224}