diff --git a/Cargo.lock b/Cargo.lock index 819311184..3d610591d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -399,29 +399,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" -[[package]] -name = "cli-table" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53f9241f288a7b12c56565f04aaeaeeab6b8923d42d99255d4ca428b4d97f89" -dependencies = [ - "cli-table-derive", - "csv", - "termcolor", - "unicode-width", -] - -[[package]] -name = "cli-table-derive" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e83a93253aaae7c74eb7428ce4faa6e219ba94886908048888701819f82fb94" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "clipboard-win" version = "4.5.0" @@ -449,6 +426,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "comfy-table" +version = "7.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a65ebfec4fb190b6f90e944a817d60499ee0744e582530e2c9900a22e591d9a" +dependencies = [ + "crossterm", + "unicode-segmentation", + "unicode-width 0.2.0", +] + [[package]] name = "comma" version = "1.0.0" @@ -595,6 +583,28 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +[[package]] +name = "crossterm" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" +dependencies = [ + "bitflags 2.8.0", + "crossterm_winapi", + "parking_lot", + "rustix", + "winapi", +] + +[[package]] +name = "crossterm_winapi" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" +dependencies = [ + "winapi", +] + [[package]] name = "crunchy" version = "0.2.2" @@ -1564,7 +1574,7 @@ version = "0.0.14" dependencies = [ "anyhow", "clap", - "cli-table", + "comfy-table", "csv", "ctrlc", "dirs", @@ -1848,7 +1858,7 @@ dependencies = [ "terminal_size", "textwrap", "thiserror 1.0.69", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -2704,7 +2714,7 @@ dependencies = [ "radix_trie", "scopeguard", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", "utf8parse", "winapi", ] @@ -3033,7 +3043,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" dependencies = [ "unicode-linebreak", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] @@ -3201,6 +3211,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" + [[package]] name = "unindent" version = "0.2.3" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 1886627e4..03857adb2 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -21,7 +21,7 @@ path = "main.rs" [dependencies] anyhow = "1.0.75" clap = { version = "4.5", features = ["derive"] } -cli-table = "0.4.7" +comfy-table = "7.1.4" dirs = "5.0.1" env_logger = "0.10.1" limbo_core = { path = "../core" } diff --git a/cli/app.rs b/cli/app.rs index 7fa7b70f0..45dd14896 100644 --- a/cli/app.rs +++ b/cli/app.rs @@ -2,8 +2,7 @@ use crate::{ import::{ImportFile, IMPORT_HELP}, opcodes_dictionary::OPCODE_DESCRIPTIONS, }; -use cli_table::format::{Border, HorizontalLine, Separator, VerticalLine}; -use cli_table::{Cell, Style, Table}; +use comfy_table::{Attribute, Cell, CellAlignment, ContentArrangement, Row, Table}; use limbo_core::{Database, LimboError, Statement, StepResult, Value}; use clap::{Parser, ValueEnum}; @@ -670,35 +669,42 @@ impl Limbo { println!("Query interrupted."); return Ok(()); } - let mut table_rows: Vec> = vec![]; + let mut table = Table::new(); + table + .set_content_arrangement(ContentArrangement::Dynamic) + .set_truncation_indicator("…") + .apply_modifier("││──├─┼┤│─┼├┤┬┴┌┐└┘"); if rows.num_columns() > 0 { - let columns = (0..rows.num_columns()) + let header = (0..rows.num_columns()) .map(|i| { - rows.get_column_name(i) - .map(|name| name.cell().bold(true)) - .unwrap_or_else(|| " ".cell()) + let name = rows.get_column_name(i).cloned().unwrap_or_default(); + Cell::new(name).add_attribute(Attribute::Bold) }) .collect::>(); - table_rows.push(columns); + table.set_header(header); } loop { match rows.step() { Ok(StepResult::Row) => { - let row = rows.row().unwrap(); - table_rows.push( - row.values - .iter() - .map(|value| match value.to_value() { - Value::Null => self.opts.null_value.clone().cell(), - Value::Integer(i) => i.to_string().cell(), - Value::Float(f) => f.to_string().cell(), - Value::Text(s) => s.cell(), - Value::Blob(b) => { - format!("{}", String::from_utf8_lossy(b)).cell() - } - }) - .collect(), - ); + let record = rows.row().unwrap(); + let mut row = Row::new(); + row.max_height(1); + for value in &record.values { + let (content, alignment) = match value.to_value() { + Value::Null => { + (self.opts.null_value.clone(), CellAlignment::Left) + } + Value::Integer(i) => (i.to_string(), CellAlignment::Right), + Value::Float(f) => (f.to_string(), CellAlignment::Right), + Value::Text(s) => (s.to_string(), CellAlignment::Left), + Value::Blob(b) => ( + String::from_utf8_lossy(b).to_string(), + CellAlignment::Left, + ), + }; + row.add_cell(Cell::new(content).set_alignment(alignment)); + } + table.add_row(row); } Ok(StepResult::IO) => { self.io.run_once()?; @@ -718,7 +724,10 @@ impl Limbo { } } } - self.print_table(table_rows); + + if table.header().is_some() { + let _ = self.write_fmt(format_args!("{}", table)); + } } }, Ok(None) => {} @@ -734,40 +743,6 @@ impl Limbo { Ok(()) } - fn print_table(&mut self, table_rows: Vec>) { - if table_rows.is_empty() { - return; - } - - let horizontal_line = HorizontalLine::new('┌', '┐', '┬', '─'); - let horizontal_line_mid = HorizontalLine::new('├', '┤', '┼', '─'); - let horizontal_line_bottom = HorizontalLine::new('└', '┘', '┴', '─'); - let vertical_line = VerticalLine::new('│'); - - let border = Border::builder() - .top(horizontal_line) - .bottom(horizontal_line_bottom) - .left(vertical_line.clone()) - .right(vertical_line.clone()) - .build(); - - let separator = Separator::builder() - .column(Some(vertical_line)) - .row(Some(horizontal_line_mid)) - .build(); - - if let Ok(table) = table_rows - .table() - .border(border) - .separator(separator) - .display() - { - let _ = self.write_fmt(format_args!("{}", table)); - } else { - let _ = self.writeln("Error displaying table."); - } - } - fn display_schema(&mut self, table: Option<&str>) -> anyhow::Result<()> { let sql = match table { Some(table_name) => format!(