use crate::hsref::Ref;
use crate::{is_tag_name, TagName};
use serde_json::json;
use serde_json::map::Map;
use serde_json::Value;
use serde_json::{to_string, to_string_pretty};
use std::cmp::Ordering;
use std::collections::HashSet;
use std::convert::TryInto;
use std::iter::FromIterator;
use thiserror::Error;
#[derive(Clone, Debug, PartialEq)]
pub struct Grid {
json: Value,
}
impl Grid {
pub fn new(rows: Vec<Value>) -> Result<Self, ParseJsonGridError> {
let mut keys = HashSet::new();
for row in &rows {
if let Some(row_object) = row.as_object() {
keys.extend(row_object.keys());
} else {
return Err(ParseJsonGridError::new(format!(
"Expected a JSON object for row but found {}",
row
)));
}
}
let mut sorted_keys = keys.into_iter().collect::<Vec<_>>();
sorted_keys.sort();
for &key in &sorted_keys {
if !is_tag_name(key) {
return Err(ParseJsonGridError::new(format!(
"Column name '{}' is not a valid tag name",
key
)));
}
}
let cols = Value::Array(
sorted_keys
.iter()
.map(|key| json!({ "name": key }))
.collect(),
);
let mut json_grid = json!({
"meta": {"ver": "3.0"},
});
let json_grid_insert =
json_grid.as_object_mut().expect("grid is a JSON Object");
let rows = Value::Array(rows);
json_grid_insert.insert("cols".to_owned(), cols);
json_grid_insert.insert("rows".to_owned(), rows);
json_grid.try_into()
}
pub(crate) fn new_internal(rows: Vec<Value>) -> Self {
Self::new(rows)
.expect("creating grids within this crate should never fail")
}
pub fn empty() -> Self {
Self::new(vec![]).expect("creating an empty grid should never fail")
}
pub fn meta(&self) -> &Map<String, Value> {
&self.json["meta"]
.as_object()
.expect("meta is a JSON Object")
}
pub fn to_meta(&self) -> Map<String, Value> {
let meta = self.json["meta"]
.as_object()
.expect("meta is a JSON Object");
meta.clone()
}
pub(crate) fn add_ref_to_meta(&mut self, hsref: &Ref) {
let meta = self.json["meta"]
.as_object_mut()
.expect("meta is a JSON Object");
meta.insert(
"id".to_owned(),
Value::String(hsref.to_encoded_json_string()),
);
}
pub fn cols(&self) -> &Vec<Value> {
&self.json["cols"].as_array().expect("cols is a JSON Array")
}
pub fn add_col<F>(&mut self, col_name: TagName, f: F)
where
F: Fn(&mut Map<String, Value>) -> Value,
{
let col_name_string = col_name.to_string();
for row in self.json["rows"]
.as_array_mut()
.expect("rows is a JSON Array")
{
let row = row.as_object_mut().expect("Each row is a JSON object");
let value = f(row);
row.insert(col_name_string.clone(), value);
}
self.add_col_names(std::slice::from_ref(&col_name));
}
fn add_col_names(&mut self, col_names: &[TagName]) {
let mut all_names: HashSet<&str> =
HashSet::from_iter(self.col_name_strs());
for col_name in col_names {
all_names.insert(col_name.as_ref());
}
let mut all_names = all_names.into_iter().collect::<Vec<_>>();
all_names.sort();
let all_objects =
all_names.iter().map(|c| json!({ "name": c })).collect();
self.json["cols"] = Value::Array(all_objects);
}
fn remove_col_names(&mut self, col_names: &[&str]) -> u32 {
let mut all_names: HashSet<&str> =
HashSet::from_iter(self.col_name_strs());
let mut removed_col_count = 0u32;
for col_name in col_names {
if all_names.remove(col_name) {
removed_col_count += 1;
}
}
let mut all_names = all_names.into_iter().collect::<Vec<_>>();
all_names.sort();
let all_objects =
all_names.iter().map(|c| json!({ "name": c })).collect();
self.json["cols"] = Value::Array(all_objects);
removed_col_count
}
pub fn to_cols(&self) -> Vec<Value> {
self.json["cols"]
.as_array()
.expect("cols is a JSON Array")
.to_vec()
}
pub fn col_names(&self) -> Vec<TagName> {
self.cols()
.iter()
.map(|col| {
let name =
col["name"].as_str().expect("col name is a JSON string");
TagName::new(name.to_owned())
.expect("col names in grid are valid tag names")
})
.collect()
}
pub fn col_name_strs(&self) -> Vec<&str> {
self.cols()
.iter()
.map(|col| col["name"].as_str().expect("col name is a JSON string"))
.collect()
}
pub fn col_to_vec(&self, col_name: &str) -> Vec<Option<&Value>> {
self.rows().iter().map(|row| row.get(col_name)).collect()
}
pub fn has_col_name(&self, name: &str) -> bool {
self.col_name_strs().contains(&name)
}
pub fn remove_col(&mut self, col_name: &str) -> bool {
if self.has_col_name(col_name) {
self.remove_cols(std::slice::from_ref(&col_name));
true
} else {
false
}
}
pub fn remove_cols(&mut self, col_names: &[&str]) -> u32 {
self.row_maps_mut().into_iter().for_each(|row| {
for &col_name in col_names {
row.remove(col_name);
}
});
self.remove_col_names(col_names)
}
pub fn keep_cols(&mut self, cols_to_keep: &[&str]) {
let cols_to_remove = self
.col_name_strs()
.into_iter()
.filter(|col_name| !cols_to_keep.contains(col_name))
.map(|col_name| col_name.to_owned())
.collect::<Vec<_>>();
let cols_to_remove: Vec<&str> =
cols_to_remove.iter().map(AsRef::as_ref).collect();
self.remove_cols(&cols_to_remove);
}
pub fn rename_col(
&mut self,
col_name: &TagName,
new_col_name: &TagName,
) -> bool {
let new_col_tag_name = new_col_name.clone();
let col_name: &str = col_name.as_ref();
let new_col_name: &str = new_col_name.as_ref();
if self.has_col_name(col_name) {
for row in self.row_maps_mut() {
if let Some(value) = row.remove(col_name) {
row.insert(new_col_name.to_owned(), value);
}
}
self.add_col_names(std::slice::from_ref(&new_col_tag_name));
self.remove_col(col_name);
true
} else {
false
}
}
pub fn rows(&self) -> &Vec<Value> {
&self.json["rows"].as_array().expect("rows is a JSON Array")
}
pub fn row_maps(&self) -> Vec<&Map<String, Value>> {
self.json["rows"]
.as_array()
.expect("rows is a JSON Array")
.iter()
.map(|row| row.as_object().expect("row is a JSON Object"))
.collect()
}
fn row_maps_mut(&mut self) -> Vec<&mut Map<String, Value>> {
self.json["rows"]
.as_array_mut()
.expect("rows is a JSON Array")
.iter_mut()
.map(|row| row.as_object_mut().expect("row is a JSON Object"))
.collect()
}
pub fn to_rows(&self) -> Vec<Value> {
self.json["rows"]
.as_array()
.expect("rows is a JSON Array")
.to_vec()
}
pub fn to_row_maps(&self) -> Vec<Map<String, Value>> {
self.row_maps()
.iter()
.map(|&row_map| row_map.clone())
.collect()
}
pub fn sort_rows<F>(&mut self, compare: F)
where
F: FnMut(&Value, &Value) -> Ordering,
{
let rows = self.json["rows"]
.as_array_mut()
.expect("rows is a JSON Array");
rows.sort_by(compare);
}
pub fn add_row(&mut self, row: Value) -> Result<(), ParseJsonGridError> {
self.add_rows(vec![row])
}
pub fn add_rows(
&mut self,
mut rows: Vec<Value>,
) -> Result<(), ParseJsonGridError> {
if rows.iter().any(|row| !row.is_object()) {
let msg =
"At least one row being added is not a JSON object".to_owned();
return Err(ParseJsonGridError::new(msg));
}
let mut new_keys: HashSet<TagName> = HashSet::new();
for row in &rows {
let row_obj = row.as_object().expect("row is an object");
for key in row_obj.keys() {
match TagName::new(key.to_string()) {
Some(tag_name) => {
new_keys.insert(tag_name);
}
None => {
let msg = format!(
"The column name {} is not a valid tag name",
key
);
return Err(ParseJsonGridError::new(msg));
}
}
}
}
let new_keys = new_keys.into_iter().collect::<Vec<_>>();
self.add_col_names(&new_keys);
let current_rows = self.json["rows"]
.as_array_mut()
.expect("rows is a JSON Array");
current_rows.append(&mut rows);
Ok(())
}
pub fn size(&self) -> usize {
self.rows().len()
}
pub fn is_empty(&self) -> bool {
self.rows().is_empty()
}
pub fn concat_grid(&mut self, grid: Grid) {
let rows = grid.to_rows();
self.add_rows(rows)
.expect("adding rows from a Grid should never fail");
}
pub fn concat_grids(&mut self, grids: Vec<Grid>) {
for grid in grids {
self.concat_grid(grid);
}
}
pub fn concat_all(mut grids: Vec<Grid>) -> Grid {
match grids.len() {
0 => Grid::empty(),
1 => grids.remove(0),
_ => {
let mut first_grid = grids.remove(0);
first_grid.concat_grids(grids);
first_grid
}
}
}
pub fn to_json_string(&self) -> String {
to_string(&self.json)
.expect("serializing grid to String should never fail")
}
pub fn to_json_string_pretty(&self) -> String {
to_string_pretty(&self.json)
.expect("serializing grid to String should never fail")
}
pub fn is_error(&self) -> bool {
if let Some(err_val) = self.meta().get("err") {
if let Some(err_str) = err_val.as_str() {
err_str == MARKER_LITERAL
} else {
false
}
} else {
false
}
}
pub fn error_trace(&self) -> Option<String> {
use crate::ValueExt;
self.meta()["errTrace"].as_hs_str().map(|s| s.to_owned())
}
pub fn to_csv_string_with_ordered_cols(
&self,
col_names: &[&str],
) -> Result<String, CsvError> {
let mut writer = csv::Writer::from_writer(vec![]);
writer.write_record(col_names)?;
for row in self.rows() {
let mut row_values = Vec::new();
for &col_name in col_names {
let value_string = match &row[col_name] {
Value::Array(_) => "<Array>".to_owned(),
Value::Bool(true) => "T".to_owned(),
Value::Bool(false) => "F".to_owned(),
Value::Null => "".to_owned(),
Value::Number(n) => n.to_string(),
Value::Object(_) => "<Object>".to_owned(),
Value::String(s) => s.to_owned(),
};
row_values.push(value_string);
}
writer.write_record(row_values)?;
}
match writer.into_inner() {
Ok(bytes) => Ok(String::from_utf8(bytes)
.expect("Bytes should be UTF8 since all input was UTF8")),
Err(err) => Err(CsvError::from(Box::new(err))),
}
}
pub fn to_csv_string(&self) -> Result<String, CsvError> {
self.to_csv_string_with_ordered_cols(&self.col_name_strs())
}
}
#[derive(Debug, Error)]
pub enum CsvError {
#[error("Error originating from the underlying CSV library: {0}")]
Internal(#[from] csv::Error),
#[error("Error consuming a CSV writer: {0}")]
Writer(#[from] Box<csv::IntoInnerError<csv::Writer<Vec<u8>>>>),
}
const MARKER_LITERAL: &str = "m:";
impl std::convert::TryFrom<Value> for Grid {
type Error = ParseJsonGridError;
fn try_from(value: Value) -> Result<Self, Self::Error> {
if !value["meta"].is_object() {
return Err(ParseJsonGridError::new(
"Could not find a JSON object for 'meta'".to_owned(),
));
};
let cols = value["cols"].as_array().ok_or_else(|| {
ParseJsonGridError::new(
"Could not find a JSON array for 'cols'".to_owned(),
)
})?;
let rows = value["rows"].as_array().ok_or_else(|| {
ParseJsonGridError::new(
"Could not find a JSON array for 'rows'".to_owned(),
)
})?;
for col in cols {
if !col.is_object() {
return Err(ParseJsonGridError::new(format!(
"Expected a JSON object for col but found {}",
col
)));
}
}
for row in rows {
if !row.is_object() {
return Err(ParseJsonGridError::new(format!(
"Expected a JSON object for row but found {}",
row
)));
}
}
Ok(Grid { json: value })
}
}
#[derive(Clone, Debug, Eq, Error, PartialEq)]
#[error("{msg}")]
pub struct ParseJsonGridError {
pub(crate) msg: String,
}
impl ParseJsonGridError {
fn new(msg: String) -> Self {
ParseJsonGridError { msg }
}
}
#[cfg(test)]
mod test {
use super::Grid;
use crate::TagName;
use serde_json::json;
#[test]
fn add_col() {
let rows = vec![
json!({"id": "abcd1234", "dis": "Hello World"}),
json!({"id": "cdef5678", "dis": "Hello Kitty"}),
];
let mut grid = Grid::new(rows).unwrap();
let new_col = TagName::new("newCol".to_owned()).unwrap();
grid.add_col(new_col, |row| {
let id = row["id"].as_str().unwrap();
let dis = row["dis"].as_str().unwrap();
json!(id.to_string() + dis)
});
assert_eq!(
grid.rows()[0]["newCol"].as_str().unwrap(),
"abcd1234Hello World"
);
assert_eq!(
grid.rows()[1]["newCol"].as_str().unwrap(),
"cdef5678Hello Kitty"
);
assert!(grid.to_json_string().contains("abcd1234Hello World"));
assert!(grid.to_json_string().contains("cdef5678Hello Kitty"));
assert!(grid
.col_names()
.contains(&TagName::new("newCol".to_owned()).unwrap()));
}
#[test]
fn add_col_overwrite_existing_col() {
let rows = vec![
json!({"id": "abcd1234", "dis": "Hello World"}),
json!({"id": "cdef5678", "dis": "Hello Kitty"}),
];
let mut grid = Grid::new(rows).unwrap();
let col_name = TagName::new("dis".to_owned()).unwrap();
grid.add_col(col_name, |row| {
let id = row["id"].as_str().unwrap();
let dis = row["dis"].as_str().unwrap();
json!(id.to_string() + dis)
});
assert_eq!(
grid.rows()[0]["dis"].as_str().unwrap(),
"abcd1234Hello World"
);
assert_eq!(
grid.rows()[1]["dis"].as_str().unwrap(),
"cdef5678Hello Kitty"
);
assert!(grid.to_json_string().contains("abcd1234Hello World"));
assert!(grid.to_json_string().contains("cdef5678Hello Kitty"));
assert_eq!(grid.col_names().len(), 2);
}
#[test]
fn col_to_vec() {
let rows = vec![
json!({"id": "a"}),
json!({"different": "thing"}),
json!({"id": "b"}),
json!({"id": "c"}),
];
let grid = Grid::new(rows).unwrap();
let col = grid.col_to_vec("id");
assert_eq!(col[0].unwrap().as_str().unwrap(), "a");
assert!(col[1].is_none());
assert_eq!(col[2].unwrap().as_str().unwrap(), "b");
assert_eq!(col[3].unwrap().as_str().unwrap(), "c");
}
#[test]
fn sort_rows() {
let rows = vec![
json!({"id": "b"}),
json!({"id": "d"}),
json!({"id": "a"}),
json!({"id": "c"}),
];
let mut grid = Grid::new(rows).unwrap();
{
let original_cols = grid
.col_to_vec("id")
.into_iter()
.map(|elem| elem.unwrap().as_str().unwrap())
.collect::<Vec<_>>();
assert_eq!(original_cols, vec!["b", "d", "a", "c"]);
}
grid.sort_rows(|row1, row2| {
let str1 = row1["id"].as_str().unwrap();
let str2 = row2["id"].as_str().unwrap();
str1.cmp(str2)
});
let new_cols = grid
.col_to_vec("id")
.into_iter()
.map(|elem| elem.unwrap().as_str().unwrap())
.collect::<Vec<_>>();
assert_eq!(new_cols, vec!["a", "b", "c", "d"]);
}
#[test]
fn bad_col_name() {
let rows = vec![json!({"id": "d"}), json!({"BadTagName": "b"})];
let grid = Grid::new(rows);
assert!(grid.is_err());
}
#[test]
fn size() {
let empty_grid = Grid::new(vec![]).unwrap();
assert_eq!(empty_grid.size(), 0);
let rows = vec![json!({"id": "1"}), json!({"id": "2"})];
let grid = Grid::new(rows).unwrap();
assert_eq!(grid.size(), 2);
}
#[test]
fn add_rows() {
let rows = vec![json!({"id": "a"})];
let mut grid = Grid::new(rows).unwrap();
let rows_to_add = vec![
json!({"id": "b"}),
json!({"id": "c", "tag2": "test"}),
json!({"tag2": "anothertest"}),
];
grid.add_rows(rows_to_add).unwrap();
let final_rows = grid.rows();
assert_eq!(final_rows[0]["id"].as_str().unwrap(), "a");
assert!(final_rows[0].get("tag2").is_none());
assert_eq!(final_rows[1]["id"].as_str().unwrap(), "b");
assert!(final_rows[1].get("tag2").is_none());
assert_eq!(final_rows[2]["id"].as_str().unwrap(), "c");
assert_eq!(final_rows[2]["tag2"].as_str().unwrap(), "test");
assert!(final_rows[3].get("id").is_none());
assert_eq!(final_rows[3]["tag2"].as_str().unwrap(), "anothertest");
assert_eq!(grid.col_name_strs(), vec!["id", "tag2"]);
}
#[test]
fn add_no_rows() {
let rows = vec![json!({"id": "a"})];
let mut grid = Grid::new(rows).unwrap();
assert_eq!(grid.size(), 1);
grid.add_rows(vec![]).unwrap();
assert_eq!(grid.size(), 1);
}
#[test]
fn add_rows_without_json_object() {
let rows = vec![json!({"id": "a"})];
let mut grid = Grid::new(rows).unwrap();
let rows_to_add = vec![
json!({"id": "b"}),
json!("this row should be an object but it isn't"),
json!({"tag2": "anothertest"}),
];
assert!(grid.add_rows(rows_to_add).is_err());
}
#[test]
fn add_rows_with_invalid_tag_name() {
let rows = vec![json!({"id": "a"})];
let mut grid = Grid::new(rows).unwrap();
let rows_to_add = vec![
json!({"id": "b"}),
json!({"THIS_IS_AN_INVALID_TAG_NAME|{}/=?+[] .": "test"}),
json!({"tag2": "anothertest"}),
];
assert!(grid.add_rows(rows_to_add).is_err());
}
#[test]
fn has_col_name() {
let rows = vec![json!({"id": "a"})];
let grid = Grid::new(rows).unwrap();
assert!(grid.has_col_name("id"));
assert!(!grid.has_col_name("doesn't exist"));
}
#[test]
fn empty_grid() {
assert!(Grid::empty().is_empty());
}
#[test]
fn concat_all() {
let g1 = Grid::new(vec![json!({"id": "a"})]).unwrap();
let g2 = Grid::new(vec![json!({"id": "b"})]).unwrap();
let g3 = Grid::new(vec![json!({"id": "c"})]).unwrap();
let g4 = Grid::new(vec![json!({"id": "d"})]).unwrap();
let grids = vec![g1, g2, g3, g4];
let final_grid = Grid::concat_all(grids);
assert_eq!(final_grid.size(), 4);
let ids = final_grid
.col_to_vec("id")
.into_iter()
.map(|v| v.unwrap().as_str().unwrap())
.collect::<Vec<_>>();
assert_eq!(ids, vec!["a", "b", "c", "d"]);
}
#[test]
fn concat_all_with_no_grids() {
let grids = vec![];
let final_grid = Grid::concat_all(grids);
assert!(final_grid.is_empty());
}
#[test]
fn concat_all_with_some_empty_grids() {
let g1 = Grid::empty();
let g2 = Grid::new(vec![json!({"id": "b"})]).unwrap();
let g3 = Grid::new(vec![json!({"id": "c"})]).unwrap();
let g4 = Grid::empty();
let grids = vec![g1, g2, g3, g4];
let final_grid = Grid::concat_all(grids);
assert_eq!(final_grid.size(), 2);
let ids = final_grid
.col_to_vec("id")
.into_iter()
.map(|v| v.unwrap().as_str().unwrap())
.collect::<Vec<_>>();
assert_eq!(ids, vec!["b", "c"]);
}
#[test]
fn remove_cols() {
let rows = vec![
json!({"id": "a", "one": 1, "two": 2}),
json!({"one": 1}),
json!({"id": "b", "two": 2}),
json!({"one": 1, "two": 2}),
json!({"untouchedColumn": "test"}),
];
let mut grid = Grid::new(rows).unwrap();
let removed_col_count = grid.remove_cols(&["one", "two", "randomCol"]);
assert_eq!(removed_col_count, 2);
assert!(grid
.rows()
.iter()
.all(|row| !row.as_object().unwrap().contains_key("one")));
assert!(grid
.rows()
.iter()
.all(|row| !row.as_object().unwrap().contains_key("two")));
assert!(grid
.rows()
.iter()
.all(|row| !row.as_object().unwrap().contains_key("randomCol")));
assert_eq!(grid.rows()[0]["id"].as_str().unwrap(), "a");
assert_eq!(grid.rows()[2]["id"].as_str().unwrap(), "b");
assert_eq!(grid.rows()[4]["untouchedColumn"].as_str().unwrap(), "test");
assert_eq!(grid.col_name_strs(), vec!["id", "untouchedColumn"]);
}
#[test]
fn remove_cols_from_empty_grid() {
let mut grid = Grid::new(vec![]).unwrap();
assert!(grid.is_empty());
let removed_col_count = grid.remove_cols(&["one", "two", "randomCol"]);
assert_eq!(removed_col_count, 0);
assert!(grid.is_empty());
let expected_col_names: Vec<&str> = vec![];
assert_eq!(grid.col_name_strs(), expected_col_names);
}
#[test]
fn keep_cols() {
let rows = vec![
json!({"id": "a", "one": 1, "two": 2}),
json!({"one": 1}),
json!({"id": "b", "two": 2}),
json!({"one": 1, "two": 2}),
json!({"anotherColumn": "test"}),
];
let mut grid = Grid::new(rows).unwrap();
grid.keep_cols(&["id", "anotherColumn", "x"]);
assert!(grid
.rows()
.iter()
.all(|row| !row.as_object().unwrap().contains_key("one")));
assert!(grid
.rows()
.iter()
.all(|row| !row.as_object().unwrap().contains_key("two")));
assert_eq!(grid.rows()[0]["id"].as_str().unwrap(), "a");
assert_eq!(grid.rows()[2]["id"].as_str().unwrap(), "b");
assert_eq!(grid.rows()[4]["anotherColumn"].as_str().unwrap(), "test");
assert_eq!(grid.col_name_strs(), vec!["anotherColumn", "id"]);
}
#[test]
fn keep_cols_from_empty_grid() {
let mut grid = Grid::new(vec![]).unwrap();
assert!(grid.is_empty());
grid.keep_cols(&["one", "two", "randomCol"]);
assert!(grid.is_empty());
let expected_col_names: Vec<&str> = vec![];
assert_eq!(grid.col_name_strs(), expected_col_names);
}
#[test]
fn rename_col() {
let rows =
vec![json!({"id": "a", "one": 1}), json!({"id": "b", "two": 2})];
let mut grid = Grid::new(rows).unwrap();
let col_name = TagName::new("id".to_owned()).unwrap();
let new_col_name = TagName::new("test".to_owned()).unwrap();
let did_rename = grid.rename_col(&col_name, &new_col_name);
assert!(did_rename);
assert!(grid
.rows()
.iter()
.all(|row| !row.as_object().unwrap().contains_key("id")));
assert_eq!(grid.rows()[0]["test"].as_str().unwrap(), "a");
assert_eq!(grid.rows()[0]["one"].as_i64().unwrap(), 1);
assert_eq!(grid.rows()[1]["test"].as_str().unwrap(), "b");
assert_eq!(grid.rows()[1]["two"].as_i64().unwrap(), 2);
assert_eq!(grid.col_name_strs(), vec!["one", "test", "two"]);
}
#[test]
fn rename_col_if_col_missing() {
let rows =
vec![json!({"id": "a", "one": 1}), json!({"id": "b", "two": 2})];
let mut grid = Grid::new(rows).unwrap();
let col_name = TagName::new("missing".to_owned()).unwrap();
let new_col_name = TagName::new("test".to_owned()).unwrap();
let did_rename = grid.rename_col(&col_name, &new_col_name);
assert_eq!(did_rename, false);
assert_eq!(grid.col_name_strs(), vec!["id", "one", "two"]);
assert_eq!(grid.rows()[0]["id"].as_str().unwrap(), "a");
assert_eq!(grid.rows()[0]["one"].as_i64().unwrap(), 1);
assert_eq!(grid.rows()[1]["id"].as_str().unwrap(), "b");
assert_eq!(grid.rows()[1]["two"].as_i64().unwrap(), 2);
}
#[test]
fn rename_col_overwriting_existing_col() {
let rows =
vec![json!({"id": "a", "one": 1}), json!({"id": "b", "two": 2})];
let mut grid = Grid::new(rows).unwrap();
let col_name = TagName::new("id".to_owned()).unwrap();
let new_col_name = TagName::new("one".to_owned()).unwrap();
let did_rename = grid.rename_col(&col_name, &new_col_name);
assert!(did_rename);
assert_eq!(grid.col_name_strs(), vec!["one", "two"]);
assert_eq!(grid.rows()[0]["one"].as_str().unwrap(), "a");
assert_eq!(grid.rows()[1]["one"].as_str().unwrap(), "b");
assert_eq!(grid.rows()[1]["two"].as_i64().unwrap(), 2);
}
}