use crate::{Error, Result, Value, VariableMap}; use comfy_table::{Cell, Color, ContentArrangement, Table as ComfyTable}; use serde::{Deserialize, Serialize}; use std::{ cmp::Ordering, fmt::{self, Display, Formatter}, }; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Table { header: Vec, rows: Vec>, primary_key_index: usize, } impl Table { pub fn new(column_names: Vec) -> Self { Table { header: column_names, rows: Vec::new(), primary_key_index: 0, } } pub fn reserve(&mut self, additional: usize) { self.rows.reserve(additional); } pub fn column_names(&self) -> &Vec { &self.header } pub fn rows(&self) -> &Vec> { &self.rows } pub fn len(&self) -> usize { self.rows.len() } pub fn is_empty(&self) -> bool { self.rows.is_empty() } pub fn sort(&mut self) { self.rows.sort(); } pub fn insert(&mut self, row: Vec) -> Result<()> { if row.len() != self.header.len() { return Err(Error::WrongColumnAmount { expected: self.header.len(), actual: row.len(), }); } self.rows.push(row); Ok(()) } pub fn remove(&mut self, index: usize) -> Result<()> { self.rows.remove(index); Ok(()) } pub fn get_row(&self, index: usize) -> Option<&Vec> { self.rows.get(index) } pub fn get(&self, value: &Value) -> Option<&Vec> { let primary_key = self.column_names().get(self.primary_key_index)?; self.get_where(primary_key, value) } pub fn select(&self, column_names: &[String]) -> Table { let mut new_table = Table::new(column_names.to_vec()); for row in &self.rows { let mut new_row = Vec::new(); for (i, value) in row.iter().enumerate() { let column_name = self.header.get(i).unwrap(); let new_table_column_index = new_table .header .iter() .enumerate() .find_map(|(index, new_column_name)| { if new_column_name == column_name { Some(index) } else { None } }); if let Some(index) = new_table_column_index { while new_row.len() < index + 1 { new_row.push(Value::Empty); } new_row[index] = value.clone(); } } new_table.insert(new_row).unwrap(); } new_table } pub fn get_where(&self, column_name: &str, expected: &Value) -> Option<&Vec> { let column_index = self.get_column_index(column_name)?; for row in &self.rows { if let Some(actual) = row.get(column_index) { if actual == expected { return Some(row); } } } None } pub fn filter(&self, column_name: &str, expected: &Value) -> Option { let mut filtered = Table::new(self.header.clone()); let column_index = self.get_column_index(column_name)?; for row in &self.rows { let actual = row.get(column_index).unwrap(); if actual == expected { let _ = filtered.insert(row.clone()); } } Some(filtered) } pub fn get_column_index(&self, column_name: &str) -> Option { let column_names = &self.header; for (i, column) in column_names.iter().enumerate() { if column == column_name { return Some(i); } } None } } impl Display for Table { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let mut table = ComfyTable::new(); table .load_preset("││──├─┼┤│ ┬┴╭╮╰╯") .set_content_arrangement(ContentArrangement::Dynamic) .set_header(&self.header); for row in &self.rows { let row = row.iter().map(|value| { let text = match value { Value::List(list) => format!("{list:?}"), Value::Map(map) => format!("Map ({} items)", map.len()), Value::Table(table) => format!("Table ({} items)", table.len()), Value::Function(_) => "Function".to_string(), Value::Empty => "Empty".to_string(), value => value.to_string(), }; let mut cell = Cell::new(text).bg(Color::Rgb { r: 40, g: 40, b: 40, }); if value.is_string() { cell = cell.fg(Color::Green); } if value.is_integer() { cell = cell.fg(Color::Blue); } if value.is_boolean() { cell = cell.fg(Color::Red); } if value.is_function() { cell = cell.fg(Color::Cyan); } cell }); table.add_row(row); } if self.header.is_empty() { table.set_header(["empty"]); } write!(f, "{table}") } } impl From<&Value> for Table { fn from(value: &Value) -> Self { match value { Value::String(string) => { let mut table = Table::new(vec!["string".to_string()]); table .insert(vec![Value::String(string.to_string())]) .unwrap(); table } Value::Float(float) => { let mut table = Table::new(vec!["float".to_string()]); table.insert(vec![Value::Float(*float)]).unwrap(); table } Value::Integer(integer) => { let mut table = Table::new(vec!["integer".to_string()]); table.insert(vec![Value::Integer(*integer)]).unwrap(); table } Value::Boolean(boolean) => { let mut table = Table::new(vec!["boolean".to_string()]); table.insert(vec![Value::Boolean(*boolean)]).unwrap(); table } Value::List(list) => Self::from(list), Value::Empty => Table::new(Vec::with_capacity(0)), Value::Map(map) => Self::from(map), Value::Table(table) => table.clone(), Value::Function(function) => { let mut table = Table::new(vec!["function".to_string()]); table .insert(vec![Value::Function(function.clone())]) .unwrap(); table } Value::Time(_) => todo!(), } } } impl From<&Vec> for Table { fn from(list: &Vec) -> Self { let mut table = Table::new(vec!["index".to_string(), "item".to_string()]); for (i, value) in list.iter().enumerate() { table .insert(vec![Value::Integer(i as i64), value.clone()]) .unwrap(); } table } } impl From<&mut Vec> for Table { fn from(list: &mut Vec) -> Self { let mut table = Table::new(vec!["index".to_string(), "item".to_string()]); for (i, value) in list.iter().enumerate() { if let Ok(list) = value.as_list() { table.insert(list.clone()).unwrap(); } else { table .insert(vec![Value::Integer(i as i64), value.clone()]) .unwrap(); } } table } } impl From for Table { fn from(map: VariableMap) -> Self { let keys = map.inner().keys().cloned().collect(); let values = map.inner().values().cloned().collect(); let mut table = Table::new(keys); table .insert(values) .expect("Failed to create Table from Map. This is a no-op."); table } } impl From<&VariableMap> for Table { fn from(map: &VariableMap) -> Self { let keys = map.inner().keys().cloned().collect(); let values = map.inner().values().cloned().collect(); let mut table = Table::new(keys); table .insert(values) .expect("Failed to create Table from Map. This is a no-op."); table } } impl From<&mut VariableMap> for Table { fn from(map: &mut VariableMap) -> Self { let keys = map.inner().keys().cloned().collect(); let values = map.inner().values().cloned().collect(); let mut table = Table::new(keys); table .insert(values) .expect("Failed to create Table from Map. This is a no-op."); table } } impl Eq for Table {} impl PartialEq for Table { fn eq(&self, other: &Self) -> bool { if self.header != other.header { return false; } self.rows == other.rows } } impl PartialOrd for Table { fn partial_cmp(&self, other: &Self) -> Option { self.header.partial_cmp(&other.header) } } impl Ord for Table { fn cmp(&self, other: &Self) -> Ordering { self.header.cmp(&other.header) } }