diff --git a/crates/types/src/array.rs b/crates/types/src/array.rs index 53a1f15..ededf25 100644 --- a/crates/types/src/array.rs +++ b/crates/types/src/array.rs @@ -1,3 +1,5 @@ +use core::ops::{Deref, DerefMut}; + use luajit as lua; use crate::kvec::{self, KVec}; @@ -56,6 +58,35 @@ impl Array { { self.0.push(value.into()); } + + /// Removes an `Object` from the `Array` and returns it. + /// + /// The removed object is replaced by the last element of the array. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + #[track_caller] + #[inline] + pub fn swap_remove(&mut self, index: usize) -> Object { + self.0.swap_remove(index) + } +} + +impl Deref for Array { + type Target = [Object]; + + #[inline] + fn deref(&self) -> &Self::Target { + self.0.as_slice() + } +} + +impl DerefMut for Array { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + self.0.as_mut_slice() + } } impl> FromIterator for Array { diff --git a/crates/types/src/dictionary.rs b/crates/types/src/dictionary.rs index 52f9fbe..c184336 100644 --- a/crates/types/src/dictionary.rs +++ b/crates/types/src/dictionary.rs @@ -10,12 +10,12 @@ use crate::Object; #[repr(transparent)] pub struct Dictionary(pub(super) KVec); -/// A key-value pair mapping a Neovim [`String`] to a Neovim [`Object`]. +/// A key-value pair mapping a [`String`] to an [`Object`]. // // https://github.com/neovim/neovim/blob/v0.9.0/src/nvim/api/private/defs.h#L122-L125 #[derive(Clone, PartialEq)] #[repr(C)] -pub(super) struct KeyValuePair { +pub struct KeyValuePair { key: crate::String, value: Object, } @@ -42,13 +42,36 @@ impl core::fmt::Debug for Dictionary { } impl Dictionary { + /// Returns a slice of all key-value pairs in the dictionary. + #[inline] + pub fn as_slice(&self) -> &[KeyValuePair] { + &self.0 + } + + /// Returns a mutable slice of all key-value pairs in the dictionary. + #[inline] + pub fn as_mut_slice(&mut self) -> &mut [KeyValuePair] { + &mut self.0 + } + /// Returns a reference to the value corresponding to the key. #[inline] pub fn get(&self, query: &Q) -> Option<&Object> where Q: ?Sized + PartialEq, { - self.iter().find_map(|(key, value)| (query == key).then_some(value)) + self.get_index(query).map(|idx| self.as_slice()[idx].value()) + } + + /// Returns the index of the key-value pair corresponding to the key. + #[inline] + pub fn get_index(&self, query: &Q) -> Option + where + Q: ?Sized + PartialEq, + { + self.keys() + .enumerate() + .find_map(|(idx, key)| (query == key).then_some(idx)) } /// Returns a mutable reference to the value corresponding to the key. @@ -57,8 +80,7 @@ impl Dictionary { where Q: ?Sized + PartialEq, { - self.iter_mut() - .find_map(|(key, value)| (query == key).then_some(value)) + self.get_index(query).map(|idx| self.as_mut_slice()[idx].value_mut()) } /// Inserts a key-value pair into the dictionary. @@ -98,6 +120,12 @@ impl Dictionary { self.0.len() } + /// Returns an iterator over the keys of the dictionary. + #[inline] + pub fn keys(&self) -> impl Iterator + '_ { + self.iter().map(|(key, _)| key) + } + /// Creates a new, empty `Dictionary`. #[inline] pub fn new() -> Self { @@ -110,6 +138,75 @@ impl Dictionary { #[allow(clippy::unnecessary_struct_initialization)] NonOwning::new(Self(KVec { ..self.0 })) } + + /// Removes a `KeyValuePair` from the `Dictionary` and returns it. + /// + /// The removed pair is replaced by the last element of the dictionary. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + #[track_caller] + #[inline] + pub fn swap_remove(&mut self, index: usize) -> KeyValuePair { + self.0.swap_remove(index) + } +} + +impl KeyValuePair { + /// Consumes the `KeyValuePair` and returns the key. + #[inline] + pub fn into_key(self) -> crate::String { + self.key + } + + /// Consumes the `KeyValuePair` and returns a `(key, value)` tuple. + #[inline] + pub fn into_tuple(self) -> (crate::String, Object) { + (self.key, self.value) + } + + /// Consumes the `KeyValuePair` and returns the value. + #[inline] + pub fn into_value(self) -> Object { + self.value + } + + /// Returns a shared reference to the key of the `KeyValuePair`. + #[inline] + pub fn key(&self) -> &crate::String { + &self.key + } + + /// Returns an exclusive reference to the key of the `KeyValuePair`. + #[inline] + pub fn key_mut(&mut self) -> &mut crate::String { + &mut self.key + } + + /// Returns references to both the key and value as a tuple. + #[inline] + pub fn tuple(&self) -> (&crate::String, &Object) { + (&self.key, &self.value) + } + + /// Returns exclusive references to both the key and value as a tuple. + #[inline] + pub fn tuple_mut(&mut self) -> (&mut crate::String, &mut Object) { + (&mut self.key, &mut self.value) + } + + /// Returns a shared reference to the value of the `KeyValuePair`. + #[inline] + pub fn value(&self) -> &Object { + &self.value + } + + /// Returns an exclusive reference to the value of the `KeyValuePair`. + #[inline] + pub fn value_mut(&mut self) -> &mut Object { + &mut self.value + } } impl core::ops::Index for Dictionary @@ -173,7 +270,7 @@ impl Iterator for DictIterator { #[inline] fn next(&mut self) -> Option { - self.0.next().map(|pair| (pair.key, pair.value)) + self.0.next().map(KeyValuePair::into_tuple) } #[inline] @@ -192,7 +289,7 @@ impl ExactSizeIterator for DictIterator { impl DoubleEndedIterator for DictIterator { #[inline] fn next_back(&mut self) -> Option { - self.0.next_back().map(|pair| (pair.key, pair.value)) + self.0.next_back().map(KeyValuePair::into_tuple) } } @@ -207,7 +304,7 @@ impl<'a> Iterator for DictIter<'a> { #[inline] fn next(&mut self) -> Option { - self.0.next().map(|pair| (&pair.key, &pair.value)) + self.0.next().map(KeyValuePair::tuple) } #[inline] @@ -226,7 +323,7 @@ impl ExactSizeIterator for DictIter<'_> { impl DoubleEndedIterator for DictIter<'_> { #[inline] fn next_back(&mut self) -> Option { - self.0.next_back().map(|pair| (&pair.key, &pair.value)) + self.0.next_back().map(KeyValuePair::tuple) } } @@ -240,7 +337,7 @@ impl<'a> Iterator for DictIterMut<'a> { #[inline] fn next(&mut self) -> Option { - self.0.next().map(|pair| (&mut pair.key, &mut pair.value)) + self.0.next().map(KeyValuePair::tuple_mut) } #[inline] @@ -259,7 +356,7 @@ impl ExactSizeIterator for DictIterMut<'_> { impl DoubleEndedIterator for DictIterMut<'_> { #[inline] fn next_back(&mut self) -> Option { - self.0.next_back().map(|pair| (&mut pair.key, &mut pair.value)) + self.0.next_back().map(KeyValuePair::tuple_mut) } } diff --git a/crates/types/src/kvec.rs b/crates/types/src/kvec.rs index 6798ca9..9a059b7 100644 --- a/crates/types/src/kvec.rs +++ b/crates/types/src/kvec.rs @@ -115,6 +115,28 @@ impl KVec { self.size += 1; } + /// Removes an element from the `KVec` and returns it. + /// + /// The removed element is replaced by the last element of the vector. + /// + /// # Panics + /// + /// Panics if `index` is out of bounds. + #[track_caller] + #[inline] + pub(crate) fn swap_remove(&mut self, index: usize) -> T { + let len = self.len(); + if index >= len { + panic!("swap_remove index is {index}, but len is {len}"); + } + unsafe { + let item = ptr::read(self.items.add(index)); + ptr::copy(self.items.add(len - 1), self.items.add(index), 1); + self.size -= 1; + item + } + } + /// Creates a new, empty `KVec` with the specified capacity. #[inline] pub(crate) fn with_capacity(capacity: usize) -> Self { @@ -150,6 +172,34 @@ impl PartialEq for KVec { } } +impl PartialEq<[T]> for KVec { + #[inline] + fn eq(&self, other: &[T]) -> bool { + self.as_slice() == other + } +} + +impl PartialEq<&[T]> for KVec { + #[inline] + fn eq(&self, other: &&[T]) -> bool { + self.as_slice() == *other + } +} + +impl PartialEq<[T; N]> for KVec { + #[inline] + fn eq(&self, other: &[T; N]) -> bool { + self == other.as_slice() + } +} + +impl PartialEq<&[T; N]> for KVec { + #[inline] + fn eq(&self, other: &&[T; N]) -> bool { + self == *other + } +} + impl Deref for KVec { type Target = [T]; @@ -427,4 +477,18 @@ mod tests { assert_eq!(Some("baz"), iter2.next().as_deref()); assert_eq!(None, iter2.next()); } + + #[test] + fn swap_remove() { + let mut kvec = KVec::from_iter([1, 2, 3, 4]); + assert_eq!(kvec.swap_remove(1), 2); + assert_eq!(kvec, &[1, 4, 3]); + } + + #[should_panic] + #[test] + fn swap_remove_oob() { + let mut kvec = KVec::from_iter([1, 2, 3, 4]); + kvec.swap_remove(kvec.len()); + } } diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs index 43318df..eb08f9b 100644 --- a/crates/types/src/lib.rs +++ b/crates/types/src/lib.rs @@ -18,7 +18,7 @@ mod string; pub use arena::{arena, arena_init, Arena}; pub use array::Array; -pub use dictionary::Dictionary; +pub use dictionary::{Dictionary, KeyValuePair}; pub use error::Error; pub use function::Function; pub use non_owning::NonOwning;