Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ use serde::{
Deserialize, Serialize,
};

use core::fmt;
use core::iter;
use core::mem;
use core::{cmp::Ordering, num::NonZeroUsize};
Expand Down Expand Up @@ -215,6 +216,22 @@ impl<T> ExactSizeIterator for Iter<'_, T> {

impl<T> core::iter::FusedIterator for Iter<'_, T> {}

pub enum RemoveError {
LengthOne,
IndexOutOfBounds { index: usize, len: usize },
}

impl fmt::Debug for RemoveError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::LengthOne => write!(f, "Cannot remove from NonEmpty of length 1"),
Self::IndexOutOfBounds { index, len } => {
write!(f, "removal index ({index}) should be < len ({len})")
}
}
}
}

impl<T> NonEmpty<T> {
/// Alias for [`NonEmpty::singleton`].
pub const fn new(e: T) -> Self {
Expand Down Expand Up @@ -338,6 +355,90 @@ impl<T> NonEmpty<T> {
}
}

/// Removes an element from the vector, shifting all elements after it to the left.
///
/// # Panics
///
/// Panics if the index is out of bounds or the tail vector is empty.
pub fn remove(&mut self, index: usize) -> T {
self.checked_remove(index).unwrap()
}

/// Checks if the element at a certain index can be removed from the vector, then removes it,
/// shifting all elements after it to the left.
///
/// # Errors
///
/// Returns `Err` if the index is out of bounds or the tail vector is empty.
///
/// # Examples
///
/// ```
/// use nonempty::NonEmpty;
///
/// let mut non_empty = nonempty![1, 2, 3];
/// assert_eq!(non_empty.checked_remove(1), Ok(2));
/// assert_eq!(non_empty, nonempty![1, 3]);
/// assert_eq!(non_empty.checked_remove(3), Err(RemoveError::IndexOutOfBounds { index: 3, len: 2 }));
/// assert_eq!(non_empty.checked_remove(0), Ok(1));
/// assert_eq!(non_empty, nonempty![3]);
/// assert_eq!(non_empty.checked_remove(0), Err(RemoveError::LengthOne));
/// ```
pub fn checked_remove(&mut self, index: usize) -> Result<T, RemoveError> {
if self.tail.is_empty() {
return Err(RemoveError::LengthOne);
}
let len = self.len();
if index >= len {
return Err(RemoveError::IndexOutOfBounds { index, len });
}
match index.checked_sub(1) {
None => Ok(mem::replace(&mut self.head, self.tail.remove(0))),
Some(tail_index) => Ok(self.tail.remove(tail_index)),
}
}

/// Removes an element from the vector, replacing it with the last element.
///
/// # Panics
///
/// Panics if the index is out of bounds or the tail vector is empty.
pub fn swap_remove(&mut self, index: usize) -> T {
self.checked_swap_remove(index).unwrap()
}

/// Checks if the element at a certain index can be removed from the vector, then removes it, replacing it with the last element.
///
/// # Errors
///
/// Returns `Err` if the index is out of bounds or the tail vector is empty.
///
/// # Examples
///
/// ```
/// use nonempty::NonEmpty;
///
/// let mut non_empty = nonempty![1, 2, 3, 4];
/// assert_eq!(non_empty.checked_swap_remove(1), Ok(2));
/// assert_eq!(non_empty, nonempty![1, 4, 3]);
/// assert_eq!(non_empty.checked_swap_remove(3), Err(RemoveError::IndexOutOfBounds { index: 3, len: 3 }));
/// assert_eq!(non_empty.checked_swap_remove(0), Ok(1));
/// assert_eq!(non_empty, nonempty![3, 4]);
/// ```
pub fn checked_swap_remove(&mut self, index: usize) -> Result<T, RemoveError> {
if self.tail.is_empty() {
return Err(RemoveError::LengthOne);
}
let len = self.len();
if index >= len {
return Err(RemoveError::IndexOutOfBounds { index, len });
}
match index.checked_sub(1) {
None => Ok(mem::replace(&mut self.head, self.tail.pop().unwrap())),
Some(tail_index) => Ok(self.tail.swap_remove(tail_index)),
}
}

/// Get the length of the list.
pub fn len(&self) -> usize {
self.tail.len() + 1
Expand Down