nvim_oxi_types/
array.rs

1use 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    /// Appends an element to the back of the array.
52    #[inline]
53    pub fn push<V>(&mut self, value: V)
54    where
55        V: Into<Object>,
56    {
57        self.0.push(value.into());
58    }
59}
60
61impl<T: Into<Object>> FromIterator<T> for Array {
62    #[inline]
63    fn from_iter<I: IntoIterator<Item = T>>(iter: I) -> Self {
64        Self(
65            iter.into_iter()
66                .map(Into::into)
67                .filter(|obj| obj.is_some())
68                .collect(),
69        )
70    }
71}
72
73impl IntoIterator for Array {
74    type Item = Object;
75    type IntoIter = ArrayIterator;
76
77    #[inline]
78    fn into_iter(self) -> Self::IntoIter {
79        ArrayIterator(self.0.into_iter())
80    }
81}
82
83/// An owning iterator over the `Object`s of a [`Array`].
84#[derive(Clone)]
85pub struct ArrayIterator(kvec::IntoIter<Object>);
86
87impl Iterator for ArrayIterator {
88    type Item = Object;
89
90    #[inline]
91    fn next(&mut self) -> Option<Self::Item> {
92        self.0.next()
93    }
94
95    #[inline]
96    fn size_hint(&self) -> (usize, Option<usize>) {
97        self.0.size_hint()
98    }
99}
100
101impl ExactSizeIterator for ArrayIterator {
102    #[inline]
103    fn len(&self) -> usize {
104        self.0.len()
105    }
106}
107
108impl DoubleEndedIterator for ArrayIterator {
109    #[inline]
110    fn next_back(&mut self) -> Option<Self::Item> {
111        self.0.next_back()
112    }
113}
114
115impl core::iter::FusedIterator for ArrayIterator {}
116
117impl lua::Poppable for Array {
118    #[inline]
119    unsafe fn pop(
120        lstate: *mut lua::ffi::lua_State,
121    ) -> Result<Self, lua::Error> {
122        use lua::ffi::*;
123
124        if lua_gettop(lstate) == 0 {
125            return Err(lua::Error::PopEmptyStack);
126        } else if lua_type(lstate, -1) != LUA_TTABLE {
127            let ty = lua_type(lstate, -1);
128            return Err(lua::Error::pop_wrong_type::<Self>(LUA_TTABLE, ty));
129        }
130
131        // TODO: check that the table is an array-like table and not a
132        // dictionary-like one?
133
134        let mut kvec = KVec::with_capacity(lua_objlen(lstate, -1));
135
136        lua_pushnil(lstate);
137
138        while lua_next(lstate, -2) != 0 {
139            kvec.push(Object::pop(lstate)?);
140        }
141
142        // Pop the table.
143        lua_pop(lstate, 1);
144
145        Ok(Self(kvec))
146    }
147}
148
149impl lua::Pushable for Array {
150    #[inline]
151    unsafe fn push(
152        self,
153        lstate: *mut lua::ffi::lua_State,
154    ) -> Result<core::ffi::c_int, lua::Error> {
155        use lua::ffi::*;
156
157        lua_createtable(lstate, self.len() as _, 0);
158
159        for (idx, obj) in self.into_iter().enumerate() {
160            obj.push(lstate)?;
161            lua_rawseti(lstate, -2, (idx + 1) as _);
162        }
163
164        Ok(1)
165    }
166}
167
168/// Implements `From<(A, B, C, ..)>` for tuples `(A, B, C, ..)` where all the
169/// elements in the tuple are `Into<Object>`.
170macro_rules! from_tuple {
171    ($($ty:ident)*) => {
172        impl <$($ty: Into<Object>),*> From<($($ty,)*)> for Array {
173            #[allow(non_snake_case)]
174            fn from(($($ty,)*): ($($ty,)*)) -> Self {
175                Self::from_iter([$($ty.into(),)*])
176            }
177        }
178    };
179}
180
181from_tuple!(A);
182from_tuple!(A B);
183from_tuple!(A B C);
184from_tuple!(A B C D);
185from_tuple!(A B C D E);
186from_tuple!(A B C D E F);
187from_tuple!(A B C D E F G);
188from_tuple!(A B C D E F G H);
189from_tuple!(A B C D E F G H I);
190from_tuple!(A B C D E F G H I J);
191from_tuple!(A B C D E F G H I J K);
192from_tuple!(A B C D E F G H I J K L);
193from_tuple!(A B C D E F G H I J K L M);
194from_tuple!(A B C D E F G H I J K L M N);
195from_tuple!(A B C D E F G H I J K L M N O);
196from_tuple!(A B C D E F G H I J K L M N O P);
197
198#[cfg(test)]
199mod tests {
200    use super::*;
201
202    #[test]
203    fn array_layout() {
204        use core::alloc::Layout;
205
206        assert_eq!(Layout::new::<Array>(), Layout::new::<KVec<Object>>());
207    }
208
209    #[test]
210    fn iter_basic() {
211        let array = Array::from_iter(["Foo", "Bar", "Baz"]);
212
213        let mut iter = array.into_iter();
214        assert_eq!(Some(Object::from("Foo")), iter.next());
215        assert_eq!(Some(Object::from("Bar")), iter.next());
216        assert_eq!(Some(Object::from("Baz")), iter.next());
217        assert_eq!(None, iter.next());
218    }
219
220    #[test]
221    fn drop_iter_halfway() {
222        let array = Array::from_iter(["Foo", "Bar", "Baz"]);
223
224        let mut iter = array.into_iter();
225        assert_eq!(Some(Object::from("Foo")), iter.next());
226    }
227
228    #[test]
229    fn empty_array() {
230        let empty = Array::default();
231        assert_eq!(0, empty.into_iter().count());
232    }
233}