nvim_oxi_types/
array.rs

1use core::ops::{Deref, DerefMut};
2
3use luajit as lua;
4
5use crate::NonOwning;
6use crate::Object;
7use crate::kvec::{self, KVec};
8
9/// A vector of Neovim [`Object`]s.
10#[derive(Clone, Default, PartialEq)]
11#[repr(transparent)]
12pub struct Array(pub(super) KVec<Object>);
13
14/// An owning iterator over the `Object`s of a [`Array`].
15#[derive(Clone)]
16pub struct ArrayIterator(kvec::IntoIter<Object>);
17
18/// The error type returned when trying to convert an [`Array`] into a tuple.
19#[derive(Debug, PartialEq, Eq, thiserror::Error)]
20pub enum ArrayFromTupleError<T> {
21    /// Not enough elements in the array.
22    #[error(
23        "not enough elements in the array, expected {expected_len} but got \
24         {actual_len}"
25    )]
26    NotEnoughElements { expected_len: usize, actual_len: usize },
27    /// The tuple element at the given index couldn't be converted into the
28    /// requested type.
29    #[error(
30        "couldn't convert tuple element at index {element_idx} into object: \
31         {error:?}"
32    )]
33    ElementFromObject { element_idx: usize, error: T },
34}
35
36impl core::fmt::Debug for Array {
37    #[inline]
38    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
39        f.debug_list().entries(self.iter()).finish()
40    }
41}
42
43impl Array {
44    /// Returns the number of elements in the array.
45    #[inline]
46    pub fn len(&self) -> usize {
47        self.0.len()
48    }
49
50    /// Returns `true` if the array contains no elements.
51    #[inline]
52    pub fn is_empty(&self) -> bool {
53        self.0.is_empty()
54    }
55
56    /// Returns an iterator over the `Object`s of the array.
57    #[inline]
58    pub fn iter(&self) -> core::slice::Iter<'_, Object> {
59        self.0.as_slice().iter()
60    }
61
62    /// Creates a new, empty `Array`.
63    #[inline]
64    pub fn new() -> Self {
65        Self(KVec::new())
66    }
67
68    /// Returns a non-owning version of this `Array`.
69    #[inline]
70    pub fn non_owning(&self) -> NonOwning<'_, Self> {
71        #[allow(clippy::unnecessary_struct_initialization)]
72        NonOwning::new(Self(KVec { ..self.0 }))
73    }
74
75    /// Appends an element to the back of the array.
76    #[inline]
77    pub fn push<V>(&mut self, value: V)
78    where
79        V: Into<Object>,
80    {
81        let value = value.into();
82        if !value.is_nil() {
83            self.0.push(value);
84        }
85    }
86
87    /// Removes an `Object` from the `Array` and returns it.
88    ///
89    /// The removed object is replaced by the last element of the array.
90    ///
91    /// # Panics
92    ///
93    /// Panics if `index` is out of bounds.
94    #[track_caller]
95    #[inline]
96    pub fn swap_remove(&mut self, index: usize) -> Object {
97        self.0.swap_remove(index)
98    }
99}
100
101impl Deref for Array {
102    type Target = [Object];
103
104    #[inline]
105    fn deref(&self) -> &Self::Target {
106        self.0.as_slice()
107    }
108}
109
110impl DerefMut for Array {
111    #[inline]
112    fn deref_mut(&mut self) -> &mut Self::Target {
113        self.0.as_mut_slice()
114    }
115}
116
117impl<T: Into<Object>> Extend<T> for Array {
118    #[inline]
119    fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
120        for value in iter {
121            self.push(value);
122        }
123    }
124}
125
126impl<T: Into<Object>> FromIterator<T> for Array {
127    #[inline]
128    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
129        let mut array = Self::new();
130        array.extend(iter);
131        array
132    }
133}
134
135impl IntoIterator for Array {
136    type Item = Object;
137    type IntoIter = ArrayIterator;
138
139    #[inline]
140    fn into_iter(self) -> Self::IntoIter {
141        ArrayIterator(self.0.into_iter())
142    }
143}
144
145impl Iterator for ArrayIterator {
146    type Item = Object;
147
148    #[inline]
149    fn next(&mut self) -> Option<Self::Item> {
150        self.0.next()
151    }
152
153    #[inline]
154    fn size_hint(&self) -> (usize, Option<usize>) {
155        self.0.size_hint()
156    }
157}
158
159impl ExactSizeIterator for ArrayIterator {
160    #[inline]
161    fn len(&self) -> usize {
162        self.0.len()
163    }
164}
165
166impl DoubleEndedIterator for ArrayIterator {
167    #[inline]
168    fn next_back(&mut self) -> Option<Self::Item> {
169        self.0.next_back()
170    }
171}
172
173impl core::iter::FusedIterator for ArrayIterator {}
174
175impl lua::Poppable for Array {
176    #[inline]
177    unsafe fn pop(lstate: *mut lua::ffi::State) -> Result<Self, lua::Error> {
178        use lua::ffi::*;
179
180        if lua_gettop(lstate) == 0 {
181            return Err(lua::Error::PopEmptyStack);
182        } else if lua_type(lstate, -1) != LUA_TTABLE {
183            let ty = lua_type(lstate, -1);
184            return Err(lua::Error::pop_wrong_type::<Self>(LUA_TTABLE, ty));
185        }
186
187        // TODO: check that the table is an array-like table and not a
188        // dictionary-like one?
189
190        let mut kvec = KVec::with_capacity(lua_objlen(lstate, -1));
191
192        lua_pushnil(lstate);
193
194        while lua_next(lstate, -2) != 0 {
195            kvec.push(Object::pop(lstate)?);
196        }
197
198        // Pop the table.
199        lua_pop(lstate, 1);
200
201        Ok(Self(kvec))
202    }
203}
204
205impl lua::Pushable for Array {
206    #[inline]
207    unsafe fn push(
208        self,
209        lstate: *mut lua::ffi::State,
210    ) -> Result<core::ffi::c_int, lua::Error> {
211        use lua::ffi::*;
212
213        lua_createtable(lstate, self.len() as _, 0);
214
215        for (idx, obj) in
216            self.into_iter().filter(|obj| !obj.is_nil()).enumerate()
217        {
218            obj.push(lstate)?;
219            lua_rawseti(lstate, -2, (idx + 1) as _);
220        }
221
222        Ok(1)
223    }
224}
225
226/// Implements `From<(A, B, C, ..)>` for tuples `(A, B, C, ..)` where all the
227/// elements in the tuple are `Into<Object>`.
228macro_rules! array_from_tuple {
229    ($($ty:ident)*) => {
230        impl <$($ty: Into<Object>),*> From<($($ty,)*)> for Array {
231            #[allow(non_snake_case)]
232            fn from(($($ty,)*): ($($ty,)*)) -> Self {
233                Self::from_iter([$($ty.into(),)*])
234            }
235        }
236    };
237}
238
239array_from_tuple!(A);
240array_from_tuple!(A B);
241array_from_tuple!(A B C);
242array_from_tuple!(A B C D);
243array_from_tuple!(A B C D E);
244array_from_tuple!(A B C D E F);
245array_from_tuple!(A B C D E F G);
246array_from_tuple!(A B C D E F G H);
247array_from_tuple!(A B C D E F G H I);
248array_from_tuple!(A B C D E F G H I J);
249array_from_tuple!(A B C D E F G H I J K);
250array_from_tuple!(A B C D E F G H I J K L);
251array_from_tuple!(A B C D E F G H I J K L M);
252array_from_tuple!(A B C D E F G H I J K L M N);
253array_from_tuple!(A B C D E F G H I J K L M N O);
254array_from_tuple!(A B C D E F G H I J K L M N O P);
255
256macro_rules! count {
257    () => {0i32};
258    ($x:tt $($xs:tt)*) => {1i32 + count!($($xs)*)};
259}
260
261/// Implements `TryFrom<Array>` for tuples `(A, B, C, ..)` where all the
262/// elements in the tuple are `TryFrom<Object>` with the same error.
263macro_rules! tuple_try_from_array {
264    ($($ty:ident)*) => {
265        impl<Error, $($ty,)*> TryFrom<Array> for ($($ty,)*)
266        where
267            $($ty: TryFrom<Object, Error = Error>,)*
268        {
269            type Error = ArrayFromTupleError<Error>;
270
271            #[inline]
272            #[allow(non_snake_case)]
273            fn try_from(array: Array) -> Result<Self, Self::Error> {
274                let actual_len = array.len();
275                let expected_len = count!($($ty)*) as usize;
276
277                if actual_len < expected_len {
278                    return Err(ArrayFromTupleError::NotEnoughElements {
279                        expected_len,
280                        actual_len
281                    });
282                }
283
284                let mut iter = array.into_iter();
285
286                $(
287                    let $ty = $ty::try_from(
288                        iter.next().expect("already checked len")
289                    ).map_err(|error| ArrayFromTupleError::ElementFromObject {
290                        element_idx: actual_len - iter.len() - 1,
291                        error
292                    })?;
293                )*
294
295                Ok(($($ty,)*))
296            }
297        }
298    };
299}
300
301tuple_try_from_array!(A);
302tuple_try_from_array!(A B);
303tuple_try_from_array!(A B C);
304tuple_try_from_array!(A B C D);
305tuple_try_from_array!(A B C D E);
306tuple_try_from_array!(A B C D E F);
307tuple_try_from_array!(A B C D E F G);
308tuple_try_from_array!(A B C D E F G H);
309tuple_try_from_array!(A B C D E F G H I);
310tuple_try_from_array!(A B C D E F G H I J);
311tuple_try_from_array!(A B C D E F G H I J K);
312tuple_try_from_array!(A B C D E F G H I J K L);
313tuple_try_from_array!(A B C D E F G H I J K L M);
314tuple_try_from_array!(A B C D E F G H I J K L M N);
315tuple_try_from_array!(A B C D E F G H I J K L M N O);
316tuple_try_from_array!(A B C D E F G H I J K L M N O P);
317
318#[cfg(test)]
319mod tests {
320    use super::*;
321
322    #[test]
323    fn array_layout() {
324        use core::alloc::Layout;
325
326        assert_eq!(Layout::new::<Array>(), Layout::new::<KVec<Object>>());
327    }
328
329    #[test]
330    fn iter_basic() {
331        let array = Array::from_iter(["Foo", "Bar", "Baz"]);
332
333        let mut iter = array.into_iter();
334        assert_eq!(Some(Object::from("Foo")), iter.next());
335        assert_eq!(Some(Object::from("Bar")), iter.next());
336        assert_eq!(Some(Object::from("Baz")), iter.next());
337        assert_eq!(None, iter.next());
338    }
339
340    #[test]
341    fn drop_iter_halfway() {
342        let array = Array::from_iter(["Foo", "Bar", "Baz"]);
343
344        let mut iter = array.into_iter();
345        assert_eq!(Some(Object::from("Foo")), iter.next());
346    }
347
348    #[test]
349    fn empty_array() {
350        let empty = Array::default();
351        assert_eq!(0, empty.into_iter().count());
352    }
353}