move more stuff to traits

This commit is contained in:
Vinzenz Schroeter 2025-07-09 22:10:11 +02:00
parent bbd1d643b9
commit cabf1d32ed
5 changed files with 171 additions and 141 deletions

View file

@ -154,33 +154,6 @@ impl From<CharGrid> for Vec<u8> {
#[cfg(test)]
mod test {
use super::*;
use crate::{CharGridExt, SetValueSeriesError};
#[test]
fn col_str() {
let mut grid = CharGrid::new(2, 3);
assert_eq!(grid.get_col_str(2), None);
assert_eq!(grid.get_col_str(1), Some(String::from("\0\0\0")));
assert_eq!(grid.set_col_str(1, "abc"), Ok(()));
assert_eq!(grid.get_col_str(1), Some(String::from("abc")));
}
#[test]
fn row_str() {
let mut grid = CharGrid::new(2, 3);
assert_eq!(grid.get_row_str(3), None);
assert_eq!(grid.get_row_str(1), Some(String::from("\0\0")));
assert_eq!(
grid.set_row_str(1, "abc"),
Err(SetValueSeriesError::InvalidLength {
expected: 2,
actual: 3
})
);
assert_eq!(grid.set_row_str(1, "ab"), Ok(()));
assert_eq!(grid.get_row_str(1), Some(String::from("ab")));
}
#[test]
fn str_to_char_grid() {
// conversion with .to_string() covers one more line

View file

@ -143,3 +143,33 @@ impl<G: GridMut<char>> CharGridMutExt for G {
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::{CharGrid, CharGridExt, CharGridMutExt, SetValueSeriesError};
#[test]
fn col_str() {
let mut grid = CharGrid::new(2, 3);
assert_eq!(grid.get_col_str(2), None);
assert_eq!(grid.get_col_str(1), Some(String::from("\0\0\0")));
assert_eq!(grid.set_col_str(1, "abc"), Ok(()));
assert_eq!(grid.get_col_str(1), Some(String::from("abc")));
}
#[test]
fn row_str() {
let mut grid = CharGrid::new(2, 3);
assert_eq!(grid.get_row_str(3), None);
assert_eq!(grid.get_row_str(1), Some(String::from("\0\0")));
assert_eq!(
grid.set_row_str(1, "abc"),
Err(SetValueSeriesError::InvalidLength {
expected: 2,
actual: 3
})
);
assert_eq!(grid.set_row_str(1, "ab"), Ok(()));
assert_eq!(grid.get_row_str(1), Some(String::from("ab")));
}
}

View file

@ -1,3 +1,5 @@
use crate::SetValueSeriesError;
/// A two-dimensional readonly grid of `T`
pub trait Grid<T> {
/// Get the current value at the specified position
@ -90,7 +92,7 @@ pub trait Grid<T> {
}
/// A two-dimensional mutable grid of `T`
pub trait GridMut<T>: Grid<T> {
pub trait GridMut<T: Clone>: Grid<T> {
/// Sets the value at the specified position
///
/// # Arguments
@ -150,4 +152,122 @@ pub trait GridMut<T>: Grid<T> {
}
}
}
/// Overwrites a column in the grid.
///
/// Returns [Err] if x is out of bounds or `col` is not of the correct size.
fn set_col(
&mut self,
x: usize,
col: &[T],
) -> Result<(), SetValueSeriesError> {
let height = self.height();
if col.len() != height {
return Err(SetValueSeriesError::InvalidLength {
expected: height,
actual: col.len(),
});
}
if x >= self.width() {
return Err(SetValueSeriesError::OutOfBounds {
size: self.width(),
index: x,
});
}
for (y, item) in col.iter().enumerate().take(height) {
self.set(x, y, item.clone());
}
Ok(())
}
/// Overwrites a row in the grid.
///
/// Returns [Err] if y is out of bounds or `row` is not of the correct size.
fn set_row(
&mut self,
y: usize,
row: &[T],
) -> Result<(), SetValueSeriesError> {
let width = self.width();
if row.len() != width {
return Err(SetValueSeriesError::InvalidLength {
expected: width,
actual: row.len(),
});
}
if y >= self.height() {
return Err(SetValueSeriesError::OutOfBounds {
size: self.height(),
index: y,
});
}
for (x, item) in row.iter().enumerate().take(width) {
self.set(x, y, item.clone());
}
Ok(())
}
}
#[cfg(test)]
mod test {
use crate::{DataRef, Grid, GridMut, SetValueSeriesError, ValueGrid};
#[test]
fn optional() {
let mut grid = ValueGrid::load(2, 2, &[0, 1, 2, 3]).unwrap();
assert!(grid.set_optional(0, 0, 5));
assert!(!grid.set_optional(0, 8, 42));
assert_eq!(grid.data_ref(), [5, 1, 2, 3]);
assert_eq!(grid.get_optional(0, 0), Some(5));
assert_eq!(grid.get_optional(0, 8), None);
}
#[test]
fn col() {
let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]).unwrap();
assert_eq!(grid.get_col(0), Some(vec![0, 2, 4]));
assert_eq!(grid.get_col(1), Some(vec![1, 3, 5]));
assert_eq!(grid.get_col(2), None);
assert_eq!(grid.set_col(0, &[5, 7, 9]), Ok(()));
assert_eq!(
grid.set_col(2, &[5, 7, 9]),
Err(SetValueSeriesError::OutOfBounds { size: 2, index: 2 })
);
assert_eq!(
grid.set_col(0, &[5, 7]),
Err(SetValueSeriesError::InvalidLength {
expected: 3,
actual: 2
})
);
assert_eq!(grid.get_col(0), Some(vec![5, 7, 9]));
}
#[test]
fn row() {
let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]).unwrap();
assert_eq!(grid.get_row(0), Some(vec![0, 1]));
assert_eq!(grid.get_row(2), Some(vec![4, 5]));
assert_eq!(grid.get_row(3), None);
assert_eq!(grid.set_row(0, &[5, 7]), Ok(()));
assert_eq!(grid.get_row(0), Some(vec![5, 7]));
assert_eq!(
grid.set_row(3, &[5, 7]),
Err(SetValueSeriesError::OutOfBounds { size: 3, index: 3 })
);
assert_eq!(
grid.set_row(2, &[5, 7, 3]),
Err(SetValueSeriesError::InvalidLength {
expected: 2,
actual: 3
})
);
}
}

View file

@ -224,66 +224,6 @@ impl<T: Value> ValueGrid<T> {
}
}
/// Overwrites a column in the grid.
///
/// Returns [Err] if x is out of bounds or `col` is not of the correct size.
pub fn set_col(
&mut self,
x: usize,
col: &[T],
) -> Result<(), SetValueSeriesError> {
if col.len() != self.height() {
return Err(SetValueSeriesError::InvalidLength {
expected: self.height(),
actual: col.len(),
});
}
let width = self.width();
if self
.data
.chunks_exact_mut(width)
.zip(col.iter())
.map(|(row, column_value)| {
row.get_mut(x).map(move |cell| *cell = *column_value)
})
.all(|cell| cell.is_some())
{
Ok(())
} else {
Err(SetValueSeriesError::OutOfBounds {
index: x,
size: width,
})
}
}
/// Overwrites a row in the grid.
///
/// Returns [Err] if y is out of bounds or `row` is not of the correct size.
pub fn set_row(
&mut self,
y: usize,
row: &[T],
) -> Result<(), SetValueSeriesError> {
let width = self.width();
if row.len() != width {
return Err(SetValueSeriesError::InvalidLength {
expected: width,
actual: row.len(),
});
}
let Some(chunk) = self.data.chunks_exact_mut(width).nth(y) else {
return Err(SetValueSeriesError::OutOfBounds {
size: self.height(),
index: y,
});
};
chunk.copy_from_slice(row);
Ok(())
}
/// Enumerates all values in the grid.
pub fn enumerate(
&self,
@ -587,59 +527,6 @@ mod tests {
assert_eq!(Some(&mut 8), vec.get_ref_mut_optional(2, 2));
}
#[test]
fn optional() {
let mut grid = ValueGrid::load(2, 2, &[0, 1, 2, 3]).unwrap();
assert!(grid.set_optional(0, 0, 5));
assert!(!grid.set_optional(0, 8, 42));
assert_eq!(grid.data, [5, 1, 2, 3]);
assert_eq!(grid.get_optional(0, 0), Some(5));
assert_eq!(grid.get_optional(0, 8), None);
}
#[test]
fn col() {
let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]).unwrap();
assert_eq!(grid.get_col(0), Some(vec![0, 2, 4]));
assert_eq!(grid.get_col(1), Some(vec![1, 3, 5]));
assert_eq!(grid.get_col(2), None);
assert_eq!(grid.set_col(0, &[5, 7, 9]), Ok(()));
assert_eq!(
grid.set_col(2, &[5, 7, 9]),
Err(SetValueSeriesError::OutOfBounds { size: 2, index: 2 })
);
assert_eq!(
grid.set_col(0, &[5, 7]),
Err(SetValueSeriesError::InvalidLength {
expected: 3,
actual: 2
})
);
assert_eq!(grid.get_col(0), Some(vec![5, 7, 9]));
}
#[test]
fn row() {
let mut grid = ValueGrid::load(2, 3, &[0, 1, 2, 3, 4, 5]).unwrap();
assert_eq!(grid.get_row(0), Some(vec![0, 1]));
assert_eq!(grid.get_row(2), Some(vec![4, 5]));
assert_eq!(grid.get_row(3), None);
assert_eq!(grid.set_row(0, &[5, 7]), Ok(()));
assert_eq!(grid.get_row(0), Some(vec![5, 7]));
assert_eq!(
grid.set_row(3, &[5, 7]),
Err(SetValueSeriesError::OutOfBounds { size: 3, index: 3 })
);
assert_eq!(
grid.set_row(2, &[5, 7, 3]),
Err(SetValueSeriesError::InvalidLength {
expected: 2,
actual: 3
})
);
}
#[test]
fn wrap() {
let grid = ValueGrid::from_vec(2, vec![0, 1, 2, 3, 4, 5]).unwrap();

View file

@ -56,6 +56,11 @@ macro_rules! define_window {
Window::new(self.grid, xs, ys)
}
/// Splits the window horizontally, returning windows to the left and right parts.
///
/// The right window fills the remaining width, which may be zero.
///
/// Returns None for out-of-bounds.
#[must_use]
pub fn split_horizontal(
&'t self,
@ -79,6 +84,11 @@ macro_rules! define_window {
Some((left, right))
}
/// Splits the window vertically, returning windows to the top and bottom parts.
///
/// The bottom window fills the remaining height, which may be zero.
///
/// Returns None for out-of-bounds.
#[must_use]
pub fn split_vertical(
&'t self,
@ -164,6 +174,11 @@ impl<TElement: Copy, TGrid: GridMut<TElement>> WindowMut<'_, TElement, TGrid> {
WindowMut::new(self.grid, xs, ys)
}
/// Splits the window horizontally, returning windows to the left and right parts.
///
/// The right window fills the remaining width, which may be zero.
///
/// Returns None for out-of-bounds.
#[must_use]
pub fn split_horizontal_mut<'t>(
&'t mut self,
@ -183,6 +198,11 @@ impl<TElement: Copy, TGrid: GridMut<TElement>> WindowMut<'_, TElement, TGrid> {
Some((left, right))
}
/// Splits the window vertically, returning windows to the top and bottom parts.
///
/// The bottom window fills the remaining height, which may be zero.
///
/// Returns None for out-of-bounds.
#[must_use]
pub fn split_vertical_mut<'t>(
&'t mut self,