1use luajit as lua;
2
3use crate::kvec::{self, KVec};
4use crate::{NonOwning, Object, ObjectKind, conversion};
5
6#[derive(Clone, Default, PartialEq)]
9#[repr(transparent)]
10pub struct Dictionary(pub(super) KVec<KeyValuePair>);
11
12#[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 #[inline]
46 pub fn as_slice(&self) -> &[KeyValuePair] {
47 &self.0
48 }
49
50 #[inline]
52 pub fn as_mut_slice(&mut self) -> &mut [KeyValuePair] {
53 &mut self.0
54 }
55
56 #[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 #[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 #[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 #[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 #[inline]
100 pub fn is_empty(&self) -> bool {
101 self.0.is_empty()
102 }
103
104 #[inline]
107 pub fn iter(&self) -> DictIter<'_> {
108 DictIter(self.0.iter())
109 }
110
111 #[inline]
114 pub fn iter_mut(&mut self) -> DictIterMut<'_> {
115 DictIterMut(self.0.iter_mut())
116 }
117
118 #[inline]
120 pub fn len(&self) -> usize {
121 self.0.len()
122 }
123
124 #[inline]
126 pub fn keys(&self) -> impl Iterator<Item = &crate::String> + '_ {
127 self.iter().map(|(key, _)| key)
128 }
129
130 #[inline]
132 pub fn new() -> Self {
133 Self(KVec::new())
134 }
135
136 #[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 #[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 #[inline]
160 pub fn into_key(self) -> crate::String {
161 self.key
162 }
163
164 #[inline]
166 pub fn into_tuple(self) -> (crate::String, Object) {
167 (self.key, self.value)
168 }
169
170 #[inline]
172 pub fn into_value(self) -> Object {
173 self.value
174 }
175
176 #[inline]
178 pub fn key(&self) -> &crate::String {
179 &self.key
180 }
181
182 #[inline]
184 pub fn key_mut(&mut self) -> &mut crate::String {
185 &mut self.key
186 }
187
188 #[inline]
190 pub fn tuple(&self) -> (&crate::String, &Object) {
191 (&self.key, &self.value)
192 }
193
194 #[inline]
196 pub fn tuple_mut(&mut self) -> (&mut crate::String, &mut Object) {
197 (&mut self.key, &mut self.value)
198 }
199
200 #[inline]
202 pub fn value(&self) -> &Object {
203 &self.value
204 }
205
206 #[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#[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#[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
339pub 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 lua_pushvalue(lstate, -1);
411
412 let key = crate::String::pop(lstate)?;
413
414 kvec.push(KeyValuePair { key, value });
415 }
416
417 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}