use grid::{Grid, Order};
use serde::{ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer};
use std::io::Write;
use std::usize;
pub use super::Cell;
pub use super::Slice;
#[derive(Clone, Debug, Default)]
pub struct Table {
grid: Grid<Cell>,
}
impl Serialize for Table {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq_rows = serializer.serialize_seq(Some(self.grid.rows()))?;
for row_iter in self.grid.iter_rows() {
let cells_in_row: Vec<&Cell> = row_iter.collect();
seq_rows.serialize_element(&cells_in_row)?;
}
seq_rows.end()
}
}
impl<'de> Deserialize<'de> for Table {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let rows_of_cells: Vec<Vec<Cell>> = Vec::<Vec<Cell>>::deserialize(deserializer)?;
let mut grid = Grid::new_with_order(0, 0, Order::RowMajor);
for row_vec in rows_of_cells {
grid.push_row(row_vec);
}
Ok(Table { grid: grid })
}
}
impl TryFrom<&str> for Table {
type Error = serde_json::Error;
fn try_from(value: &str) -> Result<Self, Self::Error> {
serde_json::from_str(value)
}
}
impl ToString for Table {
fn to_string(&self) -> String {
serde_json::to_string(self).unwrap()
}
}
impl Table {
pub fn new() -> Self {
Table {
grid: Grid::new_with_order(0, 0, Order::RowMajor)
}
}
fn grid(&self) -> &Grid<Cell> {
&self.grid
}
pub fn cell(&self, row: usize, col: usize) -> Option<Cell> {
self.grid().get(row, col).cloned()
}
pub fn mut_cell(&mut self, row: usize, col: usize) -> Option<&mut Cell> {
self.grid.get_mut(row, col)
}
pub fn cols(&self) -> usize {
self.grid().cols()
}
pub fn col(&self, col: usize) -> Option<Slice> {
if col >= self.grid.cols() { return None; }
Some(Slice::from_iter(self.grid.iter_col(col).cloned()))
}
pub fn insert_col<C: Into<Vec<Cell>>>(&mut self, idx: usize, new_col: C) {
self.grid.insert_col(idx, new_col.into());
}
pub fn push_col<C: Into<Vec<Cell>>>(&mut self, new_col: C) {
self.grid.insert_col(self.cols(), new_col.into());
}
pub fn remove_col(&mut self, idx: usize) -> Option<Slice> {
match self.grid.remove_col(idx) {
Some(cells) => Some(Slice::from(cells)),
None => None,
}
}
pub fn replace_col<C: Into<Vec<Cell>>>(&mut self, idx: usize, new_col: C) -> Option<Slice> {
let old_col = self.remove_col(idx);
self.insert_col(idx, new_col);
old_col
}
pub fn rows(&self) -> usize {
self.grid().rows()
}
pub fn row(&self, row: usize) -> Option<Slice> {
if row >= self.grid.rows() { return None; }
Some(Slice::from_iter(self.grid.iter_row(row).cloned()))
}
pub fn insert_row<C: Into<Vec<Cell>>>(&mut self, idx: usize, new_row: C) {
self.grid.insert_row(idx, new_row.into());
}
pub fn push_row<C: Into<Vec<Cell>>>(&mut self, new_row: C) {
self.grid.insert_row(self.rows(), new_row.into());
}
pub fn remove_row(&mut self, idx: usize) -> Option<Slice> {
match self.grid.remove_row(idx) {
Some(cells) => Some(Slice::from(cells)),
None => None,
}
}
pub fn replace_row<C: Into<Vec<Cell>>>(&mut self, idx: usize, new_row: C) -> Option<Slice> {
let old_row = self.remove_row(idx);
self.insert_row(idx, new_row);
old_row
}
fn _find_value<T: ?Sized>(&self, other_value: &T, max: Option<usize>) -> Vec<(usize, usize)> where for<'r> &'r T: Into<Cell> {
let mut vec: Vec<(usize, usize)> = Vec::new();
for (r, row_iter) in self.grid.iter_rows().enumerate() {
for (c, cell) in row_iter.enumerate() {
if cell.equal_value(&other_value) {
vec.push((r, c));
if vec.len() == max.unwrap_or(usize::MAX) {
return vec;
}
}
}
}
vec
}
pub fn find_value<T: ?Sized>(&self, other_value: &T) -> Vec<(usize, usize)> where for<'r> &'r T: Into<Cell> {
self._find_value(other_value, None)
}
pub fn contains_value<T: ?Sized>(&self, other_value: &T) -> bool where for<'r> &'r T: Into<Cell> {
self._find_value(other_value, Some(1)).len() > 0
}
pub fn write_csv<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
for row_iter in self.grid.iter_rows() {
let mut first_cell = true;
for cell in row_iter {
match first_cell {
true => first_cell = false,
false => writer.write_all(b",")?,
}
let s = cell.to_string();
let quotes = s.contains(r#"""#);
let escaping = s.contains(',')
|| s.contains('\n')
|| s.contains('\r');
if quotes || escaping { writer.write_all(b"\"")?; }
if quotes {
writer.write_all(s.replace(r#"""#, r#""""#).as_bytes())?;
} else {
writer.write_all(s.as_bytes())?;
}
if quotes || escaping { writer.write_all(b"\"")?; }
}
writer.write_all(b"\n")?
}
Ok(())
}
pub fn to_csv(&self) -> Result<String, std::io::Error> {
let mut writer: Vec<u8> = Vec::new();
self.write_csv(&mut writer)?;
Ok(String::from_utf8(writer).unwrap())
}
}
#[cfg(test)]
mod tests {
use rust_decimal::Decimal;
use super::*;
#[test]
fn test_table() {
let table = Table::new();
assert_eq!(table.grid().cols(), 0);
assert_eq!(table.grid().rows(), 0);
}
#[test]
fn test_json() {
let table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
assert_eq!(table.cols(), 3);
assert_eq!(table.rows(), 2);
assert_eq!(table.to_string(), r#"[["a","b","c"],["1","2","3"]]"#);
}
#[test]
fn test_cell() {
let table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
assert_eq!(table.cell(0, 1).unwrap().to_string(), "b".to_string());
assert_eq!(table.cell(1, 1).unwrap().to_decimal(), Some(Decimal::from(2)));
}
#[test]
fn test_mut_cell() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
let cell = table.mut_cell(1, 1).unwrap();
cell.replace_value(&Cell::from("d"));
assert_eq!(table.to_string(), r#"[["a","b","c"],["1","d","3"]]"#);
}
#[test]
fn test_columns() {
let table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
assert_eq!(table.cols(), 3);
let col0 = table.col(0).unwrap();
assert_eq!(col0.len(), 2);
assert_eq!(col0.cell(0).to_string(), "a".to_string());
assert_eq!(col0.cell(1).to_decimal(), Some(Decimal::from(1)));
let col1 = table.col(1).unwrap();
assert_eq!(col1.len(), 2);
assert_eq!(col1.cell(0).to_string(), "b".to_string());
assert_eq!(col1.cell(1).to_decimal(), Some(Decimal::from(2)));
let col2 = table.col(2).unwrap();
assert_eq!(col2.len(), 2);
assert_eq!(col2.cell(0).to_string(), "c".to_string());
assert_eq!(col2.cell(1).to_decimal(), Some(Decimal::from(3)));
assert!(table.col(3).is_none());
}
#[test]
fn test_insert_col() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
table.insert_col(1, vec![Cell::from("d"), Cell::from("4")]);
assert_eq!(table.to_string(), r#"[["a","d","b","c"],["1","4","2","3"]]"#);
table.insert_col(1, Slice::from(vec!["e", "5"]));
assert_eq!(table.to_string(), r#"[["a","e","d","b","c"],["1","5","4","2","3"]]"#);
}
#[test]
fn test_push_col() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
table.push_col(vec![Cell::from("d"), Cell::from("4")]);
assert_eq!(table.to_string(), r#"[["a","b","c","d"],["1","2","3","4"]]"#);
table.push_col(Slice::from(vec!["e", "5"]));
assert_eq!(table.to_string(), r#"[["a","b","c","d","e"],["1","2","3","4","5"]]"#);
}
#[test]
fn test_remove_col() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
assert!(table.remove_col(1).is_some());
assert_eq!(table.to_string(), r#"[["a","c"],["1","3"]]"#);
assert!(table.remove_col(2).is_none());
assert_eq!(table.to_string(), r#"[["a","c"],["1","3"]]"#);
}
#[test]
fn test_replace_col() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
assert!(table.replace_col(1, vec![Cell::from("d"), Cell::from("4")]).is_some());
assert_eq!(table.to_string(), r#"[["a","d","c"],["1","4","3"]]"#);
assert!(table.replace_col(1, Slice::from(vec!["e", "5"])).is_some());
assert_eq!(table.to_string(), r#"[["a","e","c"],["1","5","3"]]"#);
}
#[test]
fn test_rows() {
let table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
assert_eq!(table.rows(), 2);
let row0 = table.row(0).unwrap();
assert_eq!(row0.len(), 3);
assert_eq!(row0.cell(0).to_string(), "a".to_string());
assert_eq!(row0.cell(1).to_string(), "b".to_string());
assert_eq!(row0.cell(2).to_string(), "c".to_string());
let row1 = table.row(1).unwrap();
assert_eq!(row1.len(), 3);
assert_eq!(row1.cell(0).to_decimal(), Some(Decimal::from(1)));
assert_eq!(row1.cell(1).to_decimal(), Some(Decimal::from(2)));
assert_eq!(row1.cell(2).to_decimal(), Some(Decimal::from(3)));
assert!(table.row(2).is_none());
}
#[test]
fn test_insert_row() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
table.insert_row(1, vec![Cell::from("d"), Cell::from("e"), Cell::from("4")]);
assert_eq!(table.to_string(), r#"[["a","b","c"],["d","e","4"],["1","2","3"]]"#);
table.insert_row(1, Slice::from(vec!["f","g","5"]));
assert_eq!(table.to_string(), r#"[["a","b","c"],["f","g","5"],["d","e","4"],["1","2","3"]]"#);
}
#[test]
fn test_push_row() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
table.push_row(vec![Cell::from("d"), Cell::from("e"), Cell::from("4")]);
assert_eq!(table.to_string(), r#"[["a","b","c"],["1","2","3"],["d","e","4"]]"#);
table.push_row(Slice::from(vec!["f","g","5"]));
assert_eq!(table.to_string(), r#"[["a","b","c"],["1","2","3"],["d","e","4"],["f","g","5"]]"#);
}
#[test]
fn test_remove_row() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
table.remove_row(0);
assert_eq!(table.to_string(), r#"[["1","2","3"]]"#);
}
#[test]
fn test_replace_row() {
let mut table: Table = Table::try_from(r#"[["a","b","c"],["1","2","3"]]"#).unwrap();
table.replace_row(0, vec![Cell::from("d"), Cell::from("e"), Cell::from("4")]);
assert_eq!(table.to_string(), r#"[["d","e","4"],["1","2","3"]]"#);
table.replace_row(0, Slice::from(vec!["f","g","5"]));
assert_eq!(table.to_string(), r#"[["f","g","5"],["1","2","3"]]"#);
}
#[test]
fn test_add() {
let mut table: Table = Table::try_from(r#"[["1","2","3"],["4","5","6"],["x","y","z"]]"#).unwrap();
let slice1 = table.row(0).unwrap();
let slice2 = table.row(1).unwrap();
let mut slice3 = &slice1 + &slice2;
table.replace_row(2, slice3.clone());
assert_eq!(table.to_string(), r#"[["1","2","3"],["4","5","6"],["5","7","9"]]"#);
slice3.add_value(Decimal::from(1));
table.replace_row(2, slice3);
assert_eq!(table.to_string(), r#"[["1","2","3"],["4","5","6"],["6","8","10"]]"#);
}
#[test]
fn test_find_value() {
let table: Table = Table::try_from(r#"[["1","2","3"],["4","5","6"],["x","y","z"],["1","5","z"]]"#).unwrap();
assert_eq!(table.find_value(&Decimal::from(5)), vec![(1, 1), (3, 1)]);
assert_eq!(table.find_value("z"), vec![(2, 2), (3, 2)]);
assert_eq!(table.find_value("abc").len(), 0);
}
#[test]
fn test_contains_value() {
let table: Table = Table::try_from(r#"[["1","2","3"],["4","5","6"],["x","y","z"],["1","5","z"]]"#).unwrap();
assert_eq!(table.contains_value(&Decimal::from(5)), true);
assert_eq!(table.contains_value("z"), true);
assert_eq!(table.contains_value("abc"), false);
}
#[test]
fn test_write_csv() {
let table: Table = Table::try_from(r##"[["1","2","3"],["ano\"ther","lo\nng","stri\rng"],["xr,ay","y","z"]]"##).unwrap();
let mut writer: Vec<u8> = Vec::new();
assert!(table.write_csv(&mut writer).is_ok());
assert_eq!(writer, b"1,2,3\n\"ano\"\"ther\",\"lo\nng\",\"stri\rng\"\n\"xr,ay\",y,z\n");
}
#[test]
fn test_to_csv() {
let table: Table = Table::try_from(r##"[["1","2","3"],["ano\"ther","lo\nng","stri\rng"],["xr,ay","y","z"]]"##).unwrap();
assert_eq!(table.to_csv().unwrap(), "1,2,3\n\"ano\"\"ther\",\"lo\nng\",\"stri\rng\"\n\"xr,ay\",y,z\n");
}
}