use super::utils;
use crate::{iter::*, Position, Table};
use std::{
iter::FromIterator,
mem,
ops::{Index, IndexMut},
};
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(feature = "serde-1", derive(serde::Serialize, serde::Deserialize))]
pub struct FixedRowTable<T: Default, const ROW: usize> {
#[cfg_attr(
feature = "serde-1",
serde(
bound(
serialize = "T: serde::Serialize",
deserialize = "T: serde::Deserialize<'de>"
),
serialize_with = "utils::serialize_array",
deserialize_with = "utils::deserialize_array"
)
)]
cells: [Vec<T>; ROW],
col_cnt: usize,
}
impl<T: Default, const ROW: usize> FixedRowTable<T, ROW> {
pub fn new() -> Self {
Self::default()
}
pub fn truncate(&mut self) {
let col_cnt = self.col_cnt;
self.cells.iter_mut().for_each(|x| x.truncate(col_cnt));
}
pub fn shrink_to_fit(&mut self) {
let max_col: usize = self.cells.iter().map(|x| x.len()).max().unwrap_or_default();
self.col_cnt = max_col;
}
pub fn iter(&self) -> ZipPosition<&T, Cells<T, FixedRowTable<T, ROW>>> {
self.into_iter()
}
}
impl<T: Default, const ROW: usize> Default for FixedRowTable<T, ROW> {
fn default() -> Self {
Self {
cells: utils::default_array::<Vec<T>, ROW>(),
col_cnt: 0,
}
}
}
impl<T: Default, const ROW: usize> Table for FixedRowTable<T, ROW> {
type Data = T;
fn row_cnt(&self) -> usize {
ROW
}
fn col_cnt(&self) -> usize {
self.col_cnt
}
fn get_cell(&self, row: usize, col: usize) -> Option<&Self::Data> {
if row < ROW && col < self.col_cnt {
Some(&self.cells[row][col])
} else {
None
}
}
fn get_mut_cell(&mut self, row: usize, col: usize) -> Option<&mut Self::Data> {
if row < ROW && col < self.col_cnt {
Some(&mut self.cells[row][col])
} else {
None
}
}
fn insert_cell(&mut self, row: usize, col: usize, value: Self::Data) -> Option<Self::Data> {
if row < ROW {
if col >= self.col_cnt {
self.cells[row].resize_with(col + 1, Default::default);
self.col_cnt = col + 1;
}
Some(mem::replace(&mut self.cells[row][col], value))
} else {
None
}
}
fn remove_cell(&mut self, row: usize, col: usize) -> Option<T> {
self.insert_cell(row, col, T::default())
}
fn set_column_capacity(&mut self, capacity: usize) {
self.col_cnt = capacity;
}
}
impl<T: Default, const ROW: usize> From<[Vec<T>; ROW]> for FixedRowTable<T, ROW> {
fn from(cells: [Vec<T>; ROW]) -> Self {
let col_cnt = if ROW > 0 { cells[0].len() } else { 0 };
Self { cells, col_cnt }
}
}
impl<'a, T: Default, const ROW: usize> IntoIterator for &'a FixedRowTable<T, ROW> {
type Item = (Position, &'a T);
type IntoIter = ZipPosition<&'a T, Cells<'a, T, FixedRowTable<T, ROW>>>;
fn into_iter(self) -> Self::IntoIter {
self.cells().zip_with_position()
}
}
impl<T: Default, const ROW: usize> IntoIterator for FixedRowTable<T, ROW> {
type Item = (Position, T);
type IntoIter = ZipPosition<T, IntoCells<T, FixedRowTable<T, ROW>>>;
fn into_iter(self) -> Self::IntoIter {
self.into_cells().zip_with_position()
}
}
impl<T: Default, V: Into<T>, const ROW: usize> FromIterator<(usize, usize, V)>
for FixedRowTable<T, ROW>
{
fn from_iter<I: IntoIterator<Item = (usize, usize, V)>>(iter: I) -> Self {
let mut table = Self::new();
for (row, col, value) in iter.into_iter() {
table.insert_cell(row, col, value.into());
}
table
}
}
impl<T: Default, V: Into<T>, const ROW: usize> FromIterator<(Position, V)>
for FixedRowTable<T, ROW>
{
fn from_iter<I: IntoIterator<Item = (Position, V)>>(iter: I) -> Self {
let mut table = Self::new();
for (pos, value) in iter.into_iter() {
table.insert_cell(pos.row, pos.col, value.into());
}
table
}
}
impl<T: Default, const ROW: usize> Index<(usize, usize)> for FixedRowTable<T, ROW> {
type Output = T;
fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
self.get_cell(row, col)
.expect("Row/Column index out of range")
}
}
impl<T: Default, const ROW: usize> IndexMut<(usize, usize)> for FixedRowTable<T, ROW> {
fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
self.get_mut_cell(row, col)
.expect("Row/Column index out of range")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn row_cnt_should_match_fixed_row_size() {
let table: FixedRowTable<usize, 0> = FixedRowTable::new();
assert_eq!(table.row_cnt(), 0);
let table: FixedRowTable<usize, 4> = FixedRowTable::new();
assert_eq!(table.row_cnt(), 4);
}
#[test]
fn col_cnt_should_be_dynamic() {
let table: FixedRowTable<usize, 0> = FixedRowTable::new();
assert_eq!(table.col_cnt(), 0);
let mut table: FixedRowTable<usize, 1> = FixedRowTable::new();
table.push_column(vec![1, 2, 3]);
table.push_column(vec![1, 2, 3]);
table.push_column(vec![1, 2, 3]);
assert_eq!(table.col_cnt(), 3);
}
#[test]
fn get_cell_should_return_ref_to_cell_at_location() {
let table = FixedRowTable::from([vec!["a", "b"], vec!["c", "d"]]);
assert_eq!(table.get_cell(0, 0), Some(&"a"));
assert_eq!(table.get_cell(0, 1), Some(&"b"));
assert_eq!(table.get_cell(1, 0), Some(&"c"));
assert_eq!(table.get_cell(1, 1), Some(&"d"));
assert_eq!(table.get_cell(1, 2), None);
}
#[test]
fn get_mut_cell_should_return_mut_ref_to_cell_at_location() {
let mut table = FixedRowTable::from([vec!["a", "b"], vec!["c", "d"]]);
*table.get_mut_cell(0, 0).unwrap() = "e";
assert_eq!(table.get_cell(0, 0), Some(&"e"));
}
#[test]
fn insert_cell_should_return_previous_cell_and_overwrite_content() {
let mut table: FixedRowTable<usize, 3> = FixedRowTable::new();
assert_eq!(table.insert_cell(0, 0, 123), Some(0));
assert_eq!(table.insert_cell(0, 0, 999), Some(123));
assert_eq!(table.get_cell(0, 0), Some(&999))
}
#[test]
fn remove_cell_should_return_cell_that_is_removed() {
let mut table = FixedRowTable::from([vec![1, 2], vec![3, 4]]);
assert_eq!(table.remove_cell(0, 0), Some(1));
assert_eq!(table.remove_cell(0, 0), Some(0));
}
#[test]
fn truncate_should_remove_cells_outside_of_column_capacity_count() {
let mut table = FixedRowTable::from([
vec!["a", "b", "c"],
vec!["d", "e", "f"],
vec!["g", "h", "i"],
]);
table.truncate();
assert_eq!(
table
.iter()
.map(|(pos, x)| (pos.row, pos.col, *x))
.collect::<Vec<(usize, usize, &str)>>(),
vec![
(0, 0, "a"),
(0, 1, "b"),
(0, 2, "c"),
(1, 0, "d"),
(1, 1, "e"),
(1, 2, "f"),
(2, 0, "g"),
(2, 1, "h"),
(2, 2, "i"),
]
);
table.set_column_capacity(table.col_cnt() - 1);
table.truncate();
assert_eq!(
table
.iter()
.map(|(pos, x)| (pos.row, pos.col, *x))
.collect::<Vec<(usize, usize, &str)>>(),
vec![
(0, 0, "a"),
(0, 1, "b"),
(1, 0, "d"),
(1, 1, "e"),
(2, 0, "g"),
(2, 1, "h"),
]
);
}
#[test]
fn shrink_to_fit_should_adjust_column_count_based_on_cell_positions() {
let mut table: FixedRowTable<&'static str, 3> = FixedRowTable::new();
assert_eq!(table.row_cnt(), 3);
assert_eq!(table.col_cnt(), 0);
table.cells[0].extend(vec!["a", "b"]);
table.cells[1].extend(vec!["d", "e", "f"]);
table.cells[2].extend(vec!["g"]);
assert_eq!(table.row_cnt(), 3);
assert_eq!(table.col_cnt(), 0);
table.shrink_to_fit();
assert_eq!(table.row_cnt(), 3);
assert_eq!(table.col_cnt(), 3);
}
#[test]
fn index_by_row_and_column_should_return_cell_ref() {
let table = FixedRowTable::from([vec![1, 2, 3]]);
assert_eq!(table[(0, 1)], 2);
}
#[test]
#[should_panic]
fn index_by_row_and_column_should_panic_if_cell_not_found() {
let table = FixedRowTable::from([vec![1, 2, 3]]);
let _ = table[(1, 0)];
}
#[test]
fn index_mut_by_row_and_column_should_return_mutable_cell() {
let mut table = FixedRowTable::from([vec![1, 2, 3]]);
table[(0, 1)] = 999;
assert_eq!(table[(0, 0)], 1);
assert_eq!(table[(0, 1)], 999);
assert_eq!(table[(0, 2)], 3);
}
#[test]
#[should_panic]
fn index_mut_by_row_and_column_should_panic_if_cell_not_found() {
let mut table = FixedRowTable::from([vec![1, 2, 3]]);
table[(1, 0)] = 999;
}
}