Skip to content

Commit

Permalink
Add more methods on Array and Dictionary (#212)
Browse files Browse the repository at this point in the history
  • Loading branch information
noib3 authored Jan 3, 2025
1 parent f5f8fb5 commit 8999e51
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 12 deletions.
31 changes: 31 additions & 0 deletions crates/types/src/array.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use core::ops::{Deref, DerefMut};

use luajit as lua;

use crate::kvec::{self, KVec};
Expand Down Expand Up @@ -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<T: Into<Object>> FromIterator<T> for Array {
Expand Down
119 changes: 108 additions & 11 deletions crates/types/src/dictionary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ use crate::Object;
#[repr(transparent)]
pub struct Dictionary(pub(super) KVec<KeyValuePair>);

/// 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,
}
Expand All @@ -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<Q>(&self, query: &Q) -> Option<&Object>
where
Q: ?Sized + PartialEq<crate::String>,
{
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<Q>(&self, query: &Q) -> Option<usize>
where
Q: ?Sized + PartialEq<crate::String>,
{
self.keys()
.enumerate()
.find_map(|(idx, key)| (query == key).then_some(idx))
}

/// Returns a mutable reference to the value corresponding to the key.
Expand All @@ -57,8 +80,7 @@ impl Dictionary {
where
Q: ?Sized + PartialEq<crate::String>,
{
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.
Expand Down Expand Up @@ -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<Item = &crate::String> + '_ {
self.iter().map(|(key, _)| key)
}

/// Creates a new, empty `Dictionary`.
#[inline]
pub fn new() -> Self {
Expand All @@ -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<S> core::ops::Index<S> for Dictionary
Expand Down Expand Up @@ -173,7 +270,7 @@ impl Iterator for DictIterator {

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|pair| (pair.key, pair.value))
self.0.next().map(KeyValuePair::into_tuple)
}

#[inline]
Expand All @@ -192,7 +289,7 @@ impl ExactSizeIterator for DictIterator {
impl DoubleEndedIterator for DictIterator {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|pair| (pair.key, pair.value))
self.0.next_back().map(KeyValuePair::into_tuple)
}
}

Expand All @@ -207,7 +304,7 @@ impl<'a> Iterator for DictIter<'a> {

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|pair| (&pair.key, &pair.value))
self.0.next().map(KeyValuePair::tuple)
}

#[inline]
Expand All @@ -226,7 +323,7 @@ impl ExactSizeIterator for DictIter<'_> {
impl DoubleEndedIterator for DictIter<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|pair| (&pair.key, &pair.value))
self.0.next_back().map(KeyValuePair::tuple)
}
}

Expand All @@ -240,7 +337,7 @@ impl<'a> Iterator for DictIterMut<'a> {

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next().map(|pair| (&mut pair.key, &mut pair.value))
self.0.next().map(KeyValuePair::tuple_mut)
}

#[inline]
Expand All @@ -259,7 +356,7 @@ impl ExactSizeIterator for DictIterMut<'_> {
impl DoubleEndedIterator for DictIterMut<'_> {
#[inline]
fn next_back(&mut self) -> Option<Self::Item> {
self.0.next_back().map(|pair| (&mut pair.key, &mut pair.value))
self.0.next_back().map(KeyValuePair::tuple_mut)
}
}

Expand Down
64 changes: 64 additions & 0 deletions crates/types/src/kvec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,28 @@ impl<T> KVec<T> {
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<T>` with the specified capacity.
#[inline]
pub(crate) fn with_capacity(capacity: usize) -> Self {
Expand Down Expand Up @@ -150,6 +172,34 @@ impl<T: PartialEq> PartialEq for KVec<T> {
}
}

impl<T: PartialEq> PartialEq<[T]> for KVec<T> {
#[inline]
fn eq(&self, other: &[T]) -> bool {
self.as_slice() == other
}
}

impl<T: PartialEq> PartialEq<&[T]> for KVec<T> {
#[inline]
fn eq(&self, other: &&[T]) -> bool {
self.as_slice() == *other
}
}

impl<const N: usize, T: PartialEq> PartialEq<[T; N]> for KVec<T> {
#[inline]
fn eq(&self, other: &[T; N]) -> bool {
self == other.as_slice()
}
}

impl<const N: usize, T: PartialEq> PartialEq<&[T; N]> for KVec<T> {
#[inline]
fn eq(&self, other: &&[T; N]) -> bool {
self == *other
}
}

impl<T> Deref for KVec<T> {
type Target = [T];

Expand Down Expand Up @@ -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());
}
}
2 changes: 1 addition & 1 deletion crates/types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 8999e51

Please sign in to comment.