#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
#[deprecated(
since = "0.1.4",
note = "The content of this module has been moved to the crate root"
)]
#[doc(hidden)]
pub mod dynamic;
mod legacy;
#[allow(deprecated)]
pub use legacy::{Coord, Grid, Rect};
use core::{fmt::Display, mem};
use alloc::vec::Vec;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DynamicGrid<T> {
cells: Vec<T>,
width: usize,
}
impl<T> Default for DynamicGrid<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> DynamicGrid<T>
where
T: Default,
{
#[must_use]
pub fn new_with_default(width: usize, height: usize) -> Self {
Self::new_with(width, height, |_, _| T::default())
}
}
impl<T> DynamicGrid<T> {
#[must_use]
pub fn new() -> Self {
Self {
cells: Vec::new(),
width: 0,
}
}
#[must_use]
pub fn new_with_capacity(capacity: usize) -> Self {
Self {
cells: Vec::with_capacity(capacity),
width: 0,
}
}
#[must_use]
pub fn new_with(width: usize, height: usize, mut init: impl FnMut(usize, usize) -> T) -> Self {
Self {
cells: (0..(width * height))
.map(|i| {
let (x, y) = Self::index_to_coord(i, width);
init(x, y)
})
.collect(),
width,
}
}
pub fn new_from_iter(
width: usize,
iter: impl IntoIterator<Item = T>,
) -> Result<Self, IncompatibleRowSize> {
let cells: Vec<T> = iter.into_iter().collect();
if !cells.is_empty() && (width == 0 || cells.len() % width != 0) {
return Err(IncompatibleRowSize);
};
Ok(Self { cells, width })
}
pub fn push_row(
&mut self,
row: impl IntoIterator<Item = T>,
) -> Result<(), IncompatibleRowSize> {
let old_len = self.cells.len();
self.cells.extend(row);
if self.width == 0 {
self.width = self.cells.len();
} else if self.width != self.cells.len() - old_len {
self.cells.truncate(old_len);
return Err(IncompatibleRowSize);
}
Ok(())
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.cells.is_empty()
}
#[must_use]
pub fn width(&self) -> usize {
self.width
}
#[must_use]
pub fn height(&self) -> usize {
self.cells.len() / self.width
}
#[must_use]
pub fn get(&self, x: usize, y: usize) -> Option<&T> {
self.index(x, y).and_then(|i| self.cells.get(i))
}
#[must_use]
pub fn get_mut(&mut self, x: usize, y: usize) -> Option<&mut T> {
self.index(x, y).and_then(|i| self.cells.get_mut(i))
}
pub fn set(&mut self, x: usize, y: usize, mut new_value: T) -> Option<T> {
let cell = self.get_mut(x, y)?;
mem::swap(cell, &mut new_value);
Some(new_value)
}
#[must_use]
pub fn cells(&self) -> impl DoubleEndedIterator<Item = &T> {
self.cells.iter()
}
#[must_use]
pub fn cells_mut(&mut self) -> impl DoubleEndedIterator<Item = &mut T> {
self.cells.iter_mut()
}
#[must_use]
pub fn cells_with_coords(&self) -> impl DoubleEndedIterator<Item = ((usize, usize), &T)> {
self.cells
.iter()
.enumerate()
.map(|(i, cell)| (Self::index_to_coord(i, self.width), cell))
}
#[must_use]
pub fn cells_with_coords_mut(
&mut self,
) -> impl DoubleEndedIterator<Item = ((usize, usize), &mut T)> {
self.cells
.iter_mut()
.enumerate()
.map(|(i, cell)| (Self::index_to_coord(i, self.width), cell))
}
#[must_use]
pub fn cells_in_rect(
&self,
x: usize,
y: usize,
width: usize,
height: usize,
) -> impl DoubleEndedIterator<Item = &T> {
let width = width.min(self.width - x);
let grid_height = self.height();
(y..(y + height))
.filter(move |y| *y < grid_height)
.filter_map(move |y| self.index(x, y))
.flat_map(move |from| &self.cells[from..(from + width)])
}
#[must_use]
pub fn rows(&self) -> impl DoubleEndedIterator<Item = &[T]> {
(0..self.cells.len())
.step_by(self.width)
.map(|i| &self.cells[i..(i + self.width)])
}
fn index(&self, x: usize, y: usize) -> Option<usize> {
if x >= self.width {
None
} else {
Some(y * self.width + x)
}
}
fn index_to_coord(index: usize, width: usize) -> (usize, usize) {
(index % width, index / width)
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct IncompatibleRowSize;
impl Display for IncompatibleRowSize {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(
f,
"The row size is not compatible with the current content of the grid"
)
}
}
#[rustversion::since(1.81)]
impl core::error::Error for IncompatibleRowSize {}