nvim_oxi_types/
dictionary.rs

1use luajit as lua;
2
3use crate::kvec::{self, KVec};
4use crate::{NonOwning, Object, ObjectKind, conversion};
5
6/// A vector of Neovim
7/// `(`[`String`](crate::String)`, `[`Object`](crate::Object)`)` pairs.
8#[derive(Clone, Default, PartialEq)]
9#[repr(transparent)]
10pub struct Dictionary(pub(super) KVec<KeyValuePair>);
11
12/// A key-value pair mapping a [`String`] to an [`Object`].
13//
14// https://github.com/neovim/neovim/blob/v0.9.0/src/nvim/api/private/defs.h#L122-L125
15#[derive(Clone, PartialEq)]
16#[repr(C)]
17pub struct KeyValuePair {
18    key: crate::String,
19    value: Object,
20}
21
22impl core::fmt::Debug for Dictionary {
23    #[inline]
24    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
25        write!(f, "{{ ")?;
26
27        let num_elements = self.len();
28
29        for (idx, (key, value)) in self.iter().enumerate() {
30            write!(f, "{key}: {value:?}")?;
31
32            if idx + 1 < num_elements {
33                write!(f, ", ")?;
34            }
35        }
36
37        write!(f, " }}")?;
38
39        Ok(())
40    }
41}
42
43impl Dictionary {
44    /// Returns a slice of all key-value pairs in the dictionary.
45    #[inline]
46    pub fn as_slice(&self) -> &[KeyValuePair] {
47        &self.0
48    }
49
50    /// Returns a mutable slice of all key-value pairs in the dictionary.
51    #[inline]
52    pub fn as_mut_slice(&mut self) -> &mut [KeyValuePair] {
53        &mut self.0
54    }
55
56    /// Returns a reference to the value corresponding to the key.
57    #[inline]
58    pub fn get<Q>(&self, query: &Q) -> Option<&Object>
59    where
60        Q: ?Sized + PartialEq<crate::String>,
61    {
62        self.get_index(query).map(|idx| self.as_slice()[idx].value())
63    }
64
65    /// Returns the index of the key-value pair corresponding to the key.
66    #[inline]
67    pub fn get_index<Q>(&self, query: &Q) -> Option<usize>
68    where
69        Q: ?Sized + PartialEq<crate::String>,
70    {
71        self.keys()
72            .enumerate()
73            .find_map(|(idx, key)| (query == key).then_some(idx))
74    }
75
76    /// Returns a mutable reference to the value corresponding to the key.
77    #[inline]
78    pub fn get_mut<Q>(&mut self, query: &Q) -> Option<&mut Object>
79    where
80        Q: ?Sized + PartialEq<crate::String>,
81    {
82        self.get_index(query).map(|idx| self.as_mut_slice()[idx].value_mut())
83    }
84
85    /// Inserts a key-value pair into the dictionary.
86    #[inline]
87    pub fn insert<K, V>(&mut self, key: K, value: V)
88    where
89        K: Into<crate::String>,
90        V: Into<Object>,
91    {
92        let value = value.into();
93        if !value.is_nil() {
94            self.0.push(KeyValuePair { key: key.into(), value });
95        }
96    }
97
98    /// Returns `true` if the dictionary contains no elements.
99    #[inline]
100    pub fn is_empty(&self) -> bool {
101        self.0.is_empty()
102    }
103
104    /// Returns an iterator over the `(String, Object)` pairs of the
105    /// dictionary.
106    #[inline]
107    pub fn iter(&self) -> DictIter<'_> {
108        DictIter(self.0.iter())
109    }
110
111    /// Returns a mutable iterator over the `(String, Object)` pairs of the
112    /// dictionary.
113    #[inline]
114    pub fn iter_mut(&mut self) -> DictIterMut<'_> {
115        DictIterMut(self.0.iter_mut())
116    }
117
118    /// Returns the number of elements in the dictionary.
119    #[inline]
120    pub fn len(&self) -> usize {
121        self.0.len()
122    }
123
124    /// Returns an iterator over the keys of the dictionary.
125    #[inline]
126    pub fn keys(&self) -> impl Iterator<Item = &crate::String> + '_ {
127        self.iter().map(|(key, _)| key)
128    }
129
130    /// Creates a new, empty `Dictionary`.
131    #[inline]
132    pub fn new() -> Self {
133        Self(KVec::new())
134    }
135
136    /// Returns a non-owning version of this `Array`.
137    #[inline]
138    pub fn non_owning(&self) -> NonOwning<'_, Self> {
139        #[allow(clippy::unnecessary_struct_initialization)]
140        NonOwning::new(Self(KVec { ..self.0 }))
141    }
142
143    /// Removes a `KeyValuePair` from the `Dictionary` and returns it.
144    ///
145    /// The removed pair is replaced by the last element of the dictionary.
146    ///
147    /// # Panics
148    ///
149    /// Panics if `index` is out of bounds.
150    #[track_caller]
151    #[inline]
152    pub fn swap_remove(&mut self, index: usize) -> KeyValuePair {
153        self.0.swap_remove(index)
154    }
155}
156
157impl KeyValuePair {
158    /// Consumes the `KeyValuePair` and returns the key.
159    #[inline]
160    pub fn into_key(self) -> crate::String {
161        self.key
162    }
163
164    /// Consumes the `KeyValuePair` and returns a `(key, value)` tuple.
165    #[inline]
166    pub fn into_tuple(self) -> (crate::String, Object) {
167        (self.key, self.value)
168    }
169
170    /// Consumes the `KeyValuePair` and returns the value.
171    #[inline]
172    pub fn into_value(self) -> Object {
173        self.value
174    }
175
176    /// Returns a shared reference to the key of the `KeyValuePair`.
177    #[inline]
178    pub fn key(&self) -> &crate::String {
179        &self.key
180    }
181
182    /// Returns an exclusive reference to the key of the `KeyValuePair`.
183    #[inline]
184    pub fn key_mut(&mut self) -> &mut crate::String {
185        &mut self.key
186    }
187
188    /// Returns references to both the key and value as a tuple.
189    #[inline]
190    pub fn tuple(&self) -> (&crate::String, &Object) {
191        (&self.key, &self.value)
192    }
193
194    /// Returns exclusive references to both the key and value as a tuple.
195    #[inline]
196    pub fn tuple_mut(&mut self) -> (&mut crate::String, &mut Object) {
197        (&mut self.key, &mut self.value)
198    }
199
200    /// Returns a shared reference to the value of the `KeyValuePair`.
201    #[inline]
202    pub fn value(&self) -> &Object {
203        &self.value
204    }
205
206    /// Returns an exclusive reference to the value of the `KeyValuePair`.
207    #[inline]
208    pub fn value_mut(&mut self) -> &mut Object {
209        &mut self.value
210    }
211}
212
213impl<S> core::ops::Index<S> for Dictionary
214where
215    S: PartialEq<crate::String>,
216{
217    type Output = Object;
218
219    #[inline]
220    fn index(&self, index: S) -> &Self::Output {
221        self.get(&index).unwrap()
222    }
223}
224
225impl<S> core::ops::IndexMut<S> for Dictionary
226where
227    S: PartialEq<crate::String>,
228{
229    #[inline]
230    fn index_mut(&mut self, index: S) -> &mut Self::Output {
231        self.get_mut(&index).unwrap()
232    }
233}
234
235impl<K, V> Extend<(K, V)> for Dictionary
236where
237    K: Into<crate::String>,
238    V: Into<Object>,
239{
240    #[inline]
241    fn extend<T: IntoIterator<Item = (K, V)>>(&mut self, iter: T) {
242        for (key, value) in iter {
243            self.insert(key, value);
244        }
245    }
246}
247
248impl<K, V> FromIterator<(K, V)> for Dictionary
249where
250    K: Into<crate::String>,
251    V: Into<Object>,
252{
253    #[inline]
254    fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
255        let mut dict = Self::new();
256        dict.extend(iter);
257        dict
258    }
259}
260
261impl IntoIterator for Dictionary {
262    type Item = (crate::String, Object);
263    type IntoIter = DictIterator;
264
265    #[inline]
266    fn into_iter(self) -> Self::IntoIter {
267        DictIterator(self.0.into_iter())
268    }
269}
270
271/// An owning iterator over the `(String, Object)` pairs of a [`Dictionary`].
272#[derive(Clone)]
273pub struct DictIterator(kvec::IntoIter<KeyValuePair>);
274
275impl Iterator for DictIterator {
276    type Item = (crate::String, Object);
277
278    #[inline]
279    fn next(&mut self) -> Option<Self::Item> {
280        self.0.next().map(KeyValuePair::into_tuple)
281    }
282
283    #[inline]
284    fn size_hint(&self) -> (usize, Option<usize>) {
285        self.0.size_hint()
286    }
287}
288
289impl ExactSizeIterator for DictIterator {
290    #[inline]
291    fn len(&self) -> usize {
292        self.0.len()
293    }
294}
295
296impl DoubleEndedIterator for DictIterator {
297    #[inline]
298    fn next_back(&mut self) -> Option<Self::Item> {
299        self.0.next_back().map(KeyValuePair::into_tuple)
300    }
301}
302
303impl core::iter::FusedIterator for DictIterator {}
304
305/// An iterator over the `(String, Object)` pairs of a [`Dictionary`].
306#[derive(Clone)]
307pub struct DictIter<'a>(core::slice::Iter<'a, KeyValuePair>);
308
309impl<'a> Iterator for DictIter<'a> {
310    type Item = (&'a crate::String, &'a Object);
311
312    #[inline]
313    fn next(&mut self) -> Option<Self::Item> {
314        self.0.next().map(KeyValuePair::tuple)
315    }
316
317    #[inline]
318    fn size_hint(&self) -> (usize, Option<usize>) {
319        self.0.size_hint()
320    }
321}
322
323impl ExactSizeIterator for DictIter<'_> {
324    #[inline]
325    fn len(&self) -> usize {
326        self.0.len()
327    }
328}
329
330impl DoubleEndedIterator for DictIter<'_> {
331    #[inline]
332    fn next_back(&mut self) -> Option<Self::Item> {
333        self.0.next_back().map(KeyValuePair::tuple)
334    }
335}
336
337impl core::iter::FusedIterator for DictIter<'_> {}
338
339/// A mutable iterator over the `(String, Object)` pairs of a [`Dictionary`].
340pub struct DictIterMut<'a>(core::slice::IterMut<'a, KeyValuePair>);
341
342impl<'a> Iterator for DictIterMut<'a> {
343    type Item = (&'a mut crate::String, &'a mut Object);
344
345    #[inline]
346    fn next(&mut self) -> Option<Self::Item> {
347        self.0.next().map(KeyValuePair::tuple_mut)
348    }
349
350    #[inline]
351    fn size_hint(&self) -> (usize, Option<usize>) {
352        self.0.size_hint()
353    }
354}
355
356impl ExactSizeIterator for DictIterMut<'_> {
357    #[inline]
358    fn len(&self) -> usize {
359        self.0.len()
360    }
361}
362
363impl DoubleEndedIterator for DictIterMut<'_> {
364    #[inline]
365    fn next_back(&mut self) -> Option<Self::Item> {
366        self.0.next_back().map(KeyValuePair::tuple_mut)
367    }
368}
369
370impl core::iter::FusedIterator for DictIterMut<'_> {}
371
372impl TryFrom<Object> for Dictionary {
373    type Error = conversion::Error;
374
375    #[inline]
376    fn try_from(obj: Object) -> Result<Self, Self::Error> {
377        match obj.kind() {
378            ObjectKind::Dictionary => {
379                Ok(unsafe { obj.into_dictionary_unchecked() })
380            },
381            other => Err(conversion::Error::FromWrongType {
382                expected: "dictionary",
383                actual: other.as_static(),
384            }),
385        }
386    }
387}
388
389impl lua::Poppable for Dictionary {
390    #[inline]
391    unsafe fn pop(lstate: *mut lua::ffi::State) -> Result<Self, lua::Error> {
392        use lua::ffi::*;
393
394        if lua_gettop(lstate) == 0 {
395            return Err(lua::Error::PopEmptyStack);
396        } else if lua_type(lstate, -1) != LUA_TTABLE {
397            let ty = lua_type(lstate, -1);
398            return Err(lua::Error::pop_wrong_type::<Self>(LUA_TTABLE, ty));
399        }
400
401        let mut kvec = KVec::with_capacity(lua_objlen(lstate, -1));
402
403        lua_pushnil(lstate);
404
405        while lua_next(lstate, -2) != 0 {
406            let value = Object::pop(lstate)?;
407
408            // The following `String::pop()` will pop the key, so we push
409            // another copy on the stack for the next iteration.
410            lua_pushvalue(lstate, -1);
411
412            let key = crate::String::pop(lstate)?;
413
414            kvec.push(KeyValuePair { key, value });
415        }
416
417        // Pop the table.
418        lua_pop(lstate, 1);
419
420        Ok(Self(kvec))
421    }
422}
423
424impl lua::Pushable for Dictionary {
425    #[inline]
426    unsafe fn push(
427        self,
428        lstate: *mut lua::ffi::State,
429    ) -> Result<core::ffi::c_int, lua::Error> {
430        use lua::ffi::*;
431
432        lua_createtable(lstate, 0, self.len() as _);
433
434        for (key, obj) in self.into_iter().filter(|(_, obj)| !obj.is_nil()) {
435            lua_pushlstring(lstate, key.as_ptr(), key.len());
436            obj.push(lstate)?;
437            lua_rawset(lstate, -3);
438        }
439
440        Ok(1)
441    }
442}
443
444#[cfg(test)]
445mod tests {
446    use super::*;
447    use crate::{Object, String as NvimString};
448
449    #[test]
450    fn dict_layout() {
451        use core::alloc::Layout;
452
453        assert_eq!(
454            Layout::new::<Dictionary>(),
455            Layout::new::<KVec<KeyValuePair>>()
456        );
457    }
458
459    #[test]
460    fn iter_basic() {
461        let dict = Dictionary::from_iter([
462            ("foo", "Foo"),
463            ("bar", "Bar"),
464            ("baz", "Baz"),
465        ]);
466
467        let mut iter = dict.into_iter();
468        assert_eq!(
469            Some((NvimString::from("foo"), Object::from("Foo"))),
470            iter.next()
471        );
472        assert_eq!(
473            Some((NvimString::from("bar"), Object::from("Bar"))),
474            iter.next()
475        );
476        assert_eq!(
477            Some((NvimString::from("baz"), Object::from("Baz"))),
478            iter.next()
479        );
480        assert_eq!(None, iter.next());
481    }
482
483    #[test]
484    fn drop_iter_halfway() {
485        let dict = Dictionary::from_iter([
486            ("foo", "Foo"),
487            ("bar", "Bar"),
488            ("baz", "Baz"),
489        ]);
490
491        let mut iter = dict.into_iter();
492        assert_eq!(
493            Some((NvimString::from("foo"), Object::from("Foo"))),
494            iter.next()
495        );
496    }
497}