ascii_table-4.0.2/.cargo_vcs_info.json0000644000000001360000000000100132760ustar { "git": { "sha1": "bbcb564a7f8fb2c419b7c102bdcae1ec8972adec" }, "path_in_vcs": "" }ascii_table-4.0.2/.gitignore000064400000000000000000000000350072674642500141040ustar 00000000000000/target **/*.rs.bk Cargo.lockascii_table-4.0.2/CHANGELOG.md000064400000000000000000000012470072674642500137330ustar 00000000000000# v4.0.2 (2022-01-25) - Created a new feature `auto_table_width`. It will set the default max width of your ascii table to the width of your terminal. Note that `AsciiTable::set_max_width` will override this value. - Changed ascii table default max width from 80 to 100. # v4.0.1 (2022-01-24) - Moved color code parsing to its own feature `color_codes`. Enable this feature when you want to display colors in the terminal using the `colorful` crate. - Created a new feature `wide_characters` who will use unicode rules to determine the width of strings. Use this feature when you want to display emoji's or other wide characters. # v4.0.0 (2022-01-23) - Revamped API. ascii_table-4.0.2/Cargo.toml0000644000000024250000000000100112770ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "ascii_table" version = "4.0.2" authors = ["Gerrit Viljoen "] description = "Print ASCII tables to the terminal" homepage = "https://gitlab.com/d5b4b2/ascii-table" documentation = "https://docs.rs/ascii_table" readme = "readme.md" keywords = ["ascii", "table"] categories = ["command-line-interface"] license = "MIT" repository = "https://gitlab.com/d5b4b2/ascii-table" resolver = "2" [dependencies.lazy_static] version = "1" optional = true [dependencies.regex] version = "1" optional = true [dependencies.termion] version = "1" optional = true [dependencies.unicode-width] version = "0.1" optional = true [dev-dependencies.colorful] version = "0.2" [features] auto_table_width = ["termion"] color_codes = ["lazy_static", "regex"] wide_characters = ["unicode-width"] ascii_table-4.0.2/Cargo.toml.orig000064400000000000000000000014320072674642500150050ustar 00000000000000[package] name = "ascii_table" version = "4.0.2" authors = ["Gerrit Viljoen "] license = "MIT" edition = "2021" description = "Print ASCII tables to the terminal" repository = "https://gitlab.com/d5b4b2/ascii-table" homepage = "https://gitlab.com/d5b4b2/ascii-table" documentation = "https://docs.rs/ascii_table" readme = "readme.md" categories = ["command-line-interface"] keywords = ["ascii", "table"] [dependencies] lazy_static = { version = "1", optional = true } regex = { version = "1", optional = true } termion = { version = "1", optional = true } unicode-width = { version = "0.1", optional = true } [dev-dependencies] colorful = "0.2" [features] auto_table_width = ["termion"] color_codes = ["lazy_static", "regex"] wide_characters = ["unicode-width"] ascii_table-4.0.2/LICENSE000064400000000000000000000020420072674642500131210ustar 00000000000000Copyright (c) 2020 Gerrit Viljoen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ascii_table-4.0.2/readme.md000064400000000000000000000031020072674642500136710ustar 00000000000000# ascii-table Print ASCII tables to the terminal. ## Example ``` use ascii_table::AsciiTable; let ascii_table = AsciiTable::default(); let data = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; ascii_table.print(data); // ┌───┬───┬───┐ // │ 1 │ 2 │ 3 │ // │ 4 │ 5 │ 6 │ // │ 7 │ 8 │ 9 │ // └───┴───┴───┘ ``` ## Example ``` use std::fmt::Display; use ascii_table::{AsciiTable, Align}; let mut ascii_table = AsciiTable::default(); ascii_table.set_max_width(26); ascii_table.column(0).set_header("H1").set_align(Align::Left); ascii_table.column(1).set_header("H2").set_align(Align::Center); ascii_table.column(2).set_header("H3").set_align(Align::Right); let data: Vec> = vec![ vec![&'v', &'v', &'v'], vec![&123, &456, &789, &"abcdef"] ]; ascii_table.print(data); // ┌─────┬─────┬─────┬──────┐ // │ H1 │ H2 │ H3 │ │ // ├─────┼─────┼─────┼──────┤ // │ v │ v │ v │ │ // │ 123 │ 456 │ 789 │ abc+ │ // └─────┴─────┴─────┴──────┘ ``` ## Features - `auto_table_width`: Sets the default max width of the ascii table to the width of the terminal. - `color_codes`: Correctly calculates the width of a string when terminal color codes are present (like those from the `colorful` crate). - `wide_characters`: Correctly calculates the width of a string when wide characters are present (like emoli's). ascii_table-4.0.2/src/lib.rs000064400000000000000000000373240072674642500140320ustar 00000000000000//! Print ASCII tables to the terminal. //! //! # Example //! //! ``` //! use ascii_table::AsciiTable; //! //! let ascii_table = AsciiTable::default(); //! let data = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; //! ascii_table.print(data); //! // ┌───┬───┬───┐ //! // │ 1 │ 2 │ 3 │ //! // │ 4 │ 5 │ 6 │ //! // │ 7 │ 8 │ 9 │ //! // └───┴───┴───┘ //! ``` //! //! # Example //! //! ``` //! use std::fmt::Display; //! use ascii_table::{AsciiTable, Align}; //! //! let mut ascii_table = AsciiTable::default(); //! ascii_table.set_max_width(26); //! ascii_table.column(0).set_header("H1").set_align(Align::Left); //! ascii_table.column(1).set_header("H2").set_align(Align::Center); //! ascii_table.column(2).set_header("H3").set_align(Align::Right); //! //! let data: Vec> = vec![ //! vec![&'v', &'v', &'v'], //! vec![&123, &456, &789, &"abcdef"] //! ]; //! ascii_table.print(data); //! // ┌─────┬─────┬─────┬──────┐ //! // │ H1 │ H2 │ H3 │ │ //! // ├─────┼─────┼─────┼──────┤ //! // │ v │ v │ v │ │ //! // │ 123 │ 456 │ 789 │ abc+ │ //! // └─────┴─────┴─────┴──────┘ //! ``` //! //! ## Features //! //! - `auto_table_width`: Sets the default max width of the ascii table to the width of the terminal. //! - `color_codes`: Correctly calculates the width of a string when terminal color codes are present //! (like those from the `colorful` crate). //! - `wide_characters`: Correctly calculates the width of a string when wide characters are present //! (like emoli's). #[cfg(test)] mod test; #[cfg(feature = "color_codes")] use lazy_static::lazy_static; #[cfg(feature = "color_codes")] use regex::Regex; #[cfg(feature = "wide_characters")] use unicode_width::UnicodeWidthStr; use std::collections::BTreeMap; use std::fmt::Display; const SE: &str = "┌"; const NW: &str = "┘"; const SW: &str = "┐"; const NS: &str = "│"; const NE: &str = "└"; const EWS: &str = "┬"; const NES: &str = "├"; const NWS: &str = "┤"; const NEW: &str = "┴"; const NEWS: &str = "┼"; const EW: &str = "─"; #[derive(Clone, Debug, Eq, PartialEq)] pub struct AsciiTable { max_width: usize, columns: BTreeMap, } impl AsciiTable { pub fn set_max_width(&mut self, max_width: usize) -> &mut Self { self.max_width = max_width; self } pub fn max_width(&self) -> usize { self.max_width } pub fn column(&mut self, index: usize) -> &mut Column { self.columns.entry(index).or_default() } } impl Default for AsciiTable { fn default() -> Self { Self { max_width: default_table_width(), columns: BTreeMap::new(), } } } #[cfg(feature = "auto_table_width")] fn default_table_width() -> usize { termion::terminal_size() .map(|(width, _)| { let width: usize = width.into(); width }) .ok() .unwrap_or(100) } #[cfg(not(feature = "auto_table_width"))] fn default_table_width() -> usize { 100 } #[derive(Clone, Debug, Eq, PartialEq)] pub struct Column { header: String, align: Align, max_width: usize, } impl Column { pub fn set_header(&mut self, header: T) -> &mut Self where T: Into, { self.header = header.into(); self } pub fn header(&self) -> &str { &self.header } pub fn set_align(&mut self, align: Align) -> &mut Self { self.align = align; self } pub fn align(&self) -> Align { self.align } pub fn set_max_width(&mut self, max_width: usize) -> &mut Self { self.max_width = max_width; self } pub fn max_width(&self) -> usize { self.max_width } } impl Default for Column { fn default() -> Self { Column { header: String::new(), align: Default::default(), max_width: usize::max_value(), } } } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)] pub enum Align { Left, Center, Right, } impl Default for Align { fn default() -> Self { Align::Left } } impl AsciiTable { pub fn print(&self, data: L1) where L1: IntoIterator, L2: IntoIterator, T: Display, { print!("{}", self.format(data)) } pub fn format(&self, data: L1) -> String where L1: IntoIterator, L2: IntoIterator, T: Display, { self.format_inner(self.stringify(data)) } fn format_inner(&self, data: Vec>) -> String { let num_cols = data.iter().map(|row| row.len()).max().unwrap_or(0); if !self.valid(&data, num_cols) { return self.format_empty(); } let header = self.stringify_header(num_cols); let data = self.square_data(data, num_cols); let has_header = header.iter().any(|text| !text.is_empty()); let widths = self.column_widths(&header, &data, num_cols); let mut result = String::new(); result.push_str(&self.format_first(&widths)); if has_header { result.push_str(&self.format_header_row(&header, &widths)); result.push_str(&self.format_middle(&widths)); } for row in data { result.push_str(&self.format_row(&row, &widths)); } result.push_str(&self.format_last(&widths)); result } fn valid(&self, data: &Vec>, num_cols: usize) -> bool { if data.len() == 0 { false } else if num_cols == 0 { false } else if self.max_width < Self::smallest_width(num_cols) { false } else { true } } fn smallest_width(num_cols: usize) -> usize { ((num_cols - 1) * 3) + 4 } fn stringify(&self, data: L1) -> Vec> where L1: IntoIterator, L2: IntoIterator, T: Display, { data.into_iter() .map(|row| { row.into_iter() .map(|cell| SmartString::from(cell.to_string())) .collect() }) .collect() } fn stringify_header(&self, num_cols: usize) -> Vec { let default_conf = &Default::default(); (0..num_cols) .map(|a| SmartString::from(self.columns.get(&a).unwrap_or(default_conf).header.clone())) .collect() } fn square_data( &self, mut data: Vec>, num_cols: usize, ) -> Vec> { for row in data.iter_mut() { while row.len() < num_cols { row.push(SmartString::new()) } } data } fn column_widths( &self, header: &[SmartString], data: &[Vec], num_cols: usize, ) -> Vec { let result: Vec<_> = (0..num_cols) .map(|a| { let default_conf = &Default::default(); let conf = self.columns.get(&a).unwrap_or(default_conf); let column_width = data.iter().map(|row| row[a].width()).max().unwrap(); let header_width = header[a].width(); column_width.max(header_width).min(conf.max_width) }) .collect(); self.truncate_widths(result) } fn truncate_widths(&self, mut widths: Vec) -> Vec { let max_width = self.max_width; let table_padding = Self::smallest_width(widths.len()); while widths.iter().sum::() + table_padding > max_width && *widths.iter().max().unwrap() > 0 { let max = widths.iter().max().unwrap(); let idx = widths.iter().rposition(|x| x == max).unwrap(); widths[idx] -= 1; } widths } fn format_line(&self, row: &[SmartString], head: &str, delim: &str, tail: &str) -> String { let mut result = String::new(); result.push_str(head); for cell in row { result.push_str(&format!("{}{}", cell, delim)); } for _ in 0..delim.chars().count() { result.pop(); } result.push_str(tail); result.push('\n'); result } fn format_empty(&self) -> String { self.format_first(&vec![0]) + &self.format_line( &[SmartString::new()], &format!("{}{}", NS, ' '), &format!("{}{}{}", ' ', NS, ' '), &format!("{}{}", ' ', NS), ) + &self.format_last(&[0]) } fn format_first(&self, widths: &[usize]) -> String { let row: Vec<_> = widths .iter() .map(|&x| SmartString::from_visible(EW.repeat(x))) .collect(); self.format_line( &row, &format!("{}{}", SE, EW), &format!("{}{}{}", EW, EWS, EW), &format!("{}{}", EW, SW), ) } fn format_middle(&self, widths: &[usize]) -> String { let row: Vec<_> = widths .iter() .map(|&x| SmartString::from_visible(EW.repeat(x))) .collect(); self.format_line( &row, &format!("{}{}", NES, EW), &format!("{}{}{}", EW, NEWS, EW), &format!("{}{}", EW, NWS), ) } fn format_row(&self, row: &[SmartString], widths: &[usize]) -> String { let row: Vec<_> = (0..widths.len()) .map(|a| { let cell = &row[a]; let width = widths[a]; let default_conf = &Default::default(); let conf = self.columns.get(&a).unwrap_or(default_conf); self.format_cell(cell, width, ' ', conf.align) }) .collect(); self.format_line( &row, &format!("{}{}", NS, ' '), &format!("{}{}{}", ' ', NS, ' '), &format!("{}{}", ' ', NS), ) } fn format_header_row(&self, row: &[SmartString], widths: &[usize]) -> String { let row: Vec<_> = row .iter() .zip(widths.iter()) .map(|(cell, &width)| self.format_cell(cell, width, ' ', Align::Left)) .collect(); self.format_line( &row, &format!("{}{}", NS, ' '), &format!("{}{}{}", ' ', NS, ' '), &format!("{}{}", ' ', NS), ) } fn format_last(&self, widths: &[usize]) -> String { let row: Vec<_> = widths .iter() .map(|&x| SmartString::from_visible(EW.repeat(x))) .collect(); self.format_line( &row, &format!("{}{}", NE, EW), &format!("{}{}{}", EW, NEW, EW), &format!("{}{}", EW, NW), ) } fn format_cell(&self, text: &SmartString, len: usize, pad: char, align: Align) -> SmartString { if text.width() > len { let mut result = text.clone(); while result.width() > len { result.pop(); } if result.pop().is_some() { result.push_visible('+') } result } else { let mut result = text.clone(); match align { Align::Left => { while result.width() < len { result.push_visible(pad) } } Align::Right => { while result.width() < len { result.lpush_visible(pad) } } Align::Center => { while result.width() < len { result.push_visible(pad); if result.width() < len { result.lpush_visible(pad) } } } } result } } } #[cfg(feature = "color_codes")] lazy_static! { static ref COLOR_CODE_PARSER: Regex = Regex::new("\u{1b}\\[([0-9]+;)*[0-9]+m").expect("Regex compilation error"); } #[derive(Clone, Debug)] struct SmartString { fragments: Vec, } #[derive(Clone, Debug)] struct SmartStringFragment { string: String, visible: bool, } impl SmartString { fn new() -> Self { Self { fragments: Vec::new(), } } #[cfg(feature = "color_codes")] fn from(string: String) -> Self { let mut fragments = Vec::new(); let mut last = 0; for r#match in COLOR_CODE_PARSER.find_iter(&string) { let start = r#match.start(); let end = r#match.end(); if last < start { fragments.push(SmartStringFragment::new(&string[last..start], true)); } fragments.push(SmartStringFragment::new(&string[start..end], false)); last = end; } if last < string.len() { fragments.push(SmartStringFragment::new(&string[last..], true)); } Self { fragments } } #[cfg(not(feature = "color_codes"))] fn from(string: String) -> Self { Self { fragments: vec![SmartStringFragment::new(string, true)], } } fn from_visible(string: String) -> Self { Self { fragments: vec![SmartStringFragment::new(string, true)], } } fn width(&self) -> usize { self.fragments .iter() .filter(|fragment| fragment.visible) .map(|fragment| fragment.width()) .sum() } fn is_empty(&self) -> bool { self.fragments .iter() .filter(|fragment| fragment.visible) .all(|fragment| fragment.string.is_empty()) } fn pop(&mut self) -> Option { self.fragments .iter_mut() .filter(|fragment| fragment.visible && !fragment.string.is_empty()) .last() .and_then(|fragment| fragment.string.pop()) } fn push_visible(&mut self, ch: char) { let last_fragment = self .fragments .iter_mut() .filter(|fragment| fragment.visible) .map(|fragment| &mut fragment.string) .last(); if let Some(fragment) = last_fragment { fragment.push(ch); } else { self.fragments.push(SmartStringFragment::new(ch, true)); } } fn lpush_visible(&mut self, ch: char) { let first_fragment = self .fragments .iter_mut() .filter(|fragment| fragment.visible) .map(|fragment| &mut fragment.string) .next(); if let Some(fragment) = first_fragment { fragment.insert(0, ch); } else { self.fragments.insert(0, SmartStringFragment::new(ch, true)); } } } impl Display for SmartString { fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error> { self.fragments .iter() .try_for_each(|fragment| fragment.string.fmt(fmt)) } } impl SmartStringFragment { fn new(string: T, visible: bool) -> Self where T: Into, { Self { string: string.into(), visible, } } #[cfg(feature = "wide_characters")] fn width(&self) -> usize { UnicodeWidthStr::width(self.string.as_str()) } #[cfg(not(feature = "wide_characters"))] fn width(&self) -> usize { self.string.chars().count() } } ascii_table-4.0.2/src/test.rs000064400000000000000000000515640072674642500142450ustar 00000000000000#[cfg(feature = "color_codes")] use colorful::Color; #[cfg(feature = "color_codes")] use colorful::Colorful; use crate::Align::*; use crate::AsciiTable; use std::fmt::Display; fn cube_config() -> AsciiTable { let mut result = AsciiTable::default(); result.column(0).set_header("a"); result.column(1).set_header("b"); result.column(2).set_header("c"); result } #[test] fn empty_rows() { let config = AsciiTable::default(); let input: Vec> = vec![]; let expected = "┌──┐\n\ │ │\n\ └──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn empty_columns() { let config = AsciiTable::default(); let input: Vec> = vec![vec![]]; let expected = "┌──┐\n\ │ │\n\ └──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn cube_with_header() { let config = cube_config(); let input = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; let expected = "┌───┬───┬───┐\n\ │ a │ b │ c │\n\ ├───┼───┼───┤\n\ │ 1 │ 2 │ 3 │\n\ │ 4 │ 5 │ 6 │\n\ │ 7 │ 8 │ 9 │\n\ └───┴───┴───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn cube_with_no_header() { let config = AsciiTable::default(); let input = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; let expected = "┌───┬───┬───┐\n\ │ 1 │ 2 │ 3 │\n\ │ 4 │ 5 │ 6 │\n\ │ 7 │ 8 │ 9 │\n\ └───┴───┴───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn one_cell() { let config = AsciiTable::default(); let input = vec![&[1]]; let expected = "┌───┐\n\ │ 1 │\n\ └───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn smallest_cell() { let mut config = AsciiTable::default(); config.set_max_width(4); let input = vec![&[123]]; let expected = "┌──┐\n\ │ │\n\ └──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn smallest_cell2() { let mut config = AsciiTable::default(); config.column(0).set_max_width(0); let input = vec![&[123]]; let expected = "┌──┐\n\ │ │\n\ └──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn smallest_cube() { let mut config = AsciiTable::default(); config.set_max_width(10); let input = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; let expected = "┌──┬──┬──┐\n\ │ │ │ │\n\ │ │ │ │\n\ │ │ │ │\n\ └──┴──┴──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn smallest_cube2() { let mut config = AsciiTable::default(); config.column(0).set_max_width(0); config.column(1).set_max_width(0); config.column(2).set_max_width(0); let input = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; let expected = "┌──┬──┬──┐\n\ │ │ │ │\n\ │ │ │ │\n\ │ │ │ │\n\ └──┴──┴──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn show_no_content_for_cell() { let mut config = AsciiTable::default(); config.set_max_width(5); let input = vec![&[123]]; let expected = "┌───┐\n\ │ + │\n\ └───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn show_no_content_for_cell2() { let mut config = AsciiTable::default(); config.column(0).set_max_width(1); let input = vec![&[123]]; let expected = "┌───┐\n\ │ + │\n\ └───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn show_one_character_for_cell() { let mut config = AsciiTable::default(); config.set_max_width(6); let input = vec![&[123]]; let expected = "┌────┐\n\ │ 1+ │\n\ └────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn show_one_character_for_cell2() { let mut config = AsciiTable::default(); config.column(0).set_max_width(2); let input = vec![&[123]]; let expected = "┌────┐\n\ │ 1+ │\n\ └────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn smallest_cell_with_header() { let mut config = AsciiTable::default(); config.set_max_width(4); config.column(0).set_header("foo"); let input = vec![&[123]]; let expected = "┌──┐\n\ │ │\n\ ├──┤\n\ │ │\n\ └──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn smallest_cell_with_header2() { let mut config = AsciiTable::default(); config.column(0).set_max_width(0).set_header("foo"); let input = vec![&[123]]; let expected = "┌──┐\n\ │ │\n\ ├──┤\n\ │ │\n\ └──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn smallest_cube_with_header() { let mut config = AsciiTable::default(); config.set_max_width(10); config.column(0).set_header("abc"); config.column(1).set_header("def"); config.column(2).set_header("ghi"); let input = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; let expected = "┌──┬──┬──┐\n\ │ │ │ │\n\ ├──┼──┼──┤\n\ │ │ │ │\n\ │ │ │ │\n\ │ │ │ │\n\ └──┴──┴──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn smallest_cube_with_header2() { let mut config = AsciiTable::default(); config.column(0).set_header("abc").set_max_width(0); config.column(1).set_header("def").set_max_width(0); config.column(2).set_header("ghi").set_max_width(0); let input = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; let expected = "┌──┬──┬──┐\n\ │ │ │ │\n\ ├──┼──┼──┤\n\ │ │ │ │\n\ │ │ │ │\n\ │ │ │ │\n\ └──┴──┴──┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn show_no_content_for_header() { let mut config = AsciiTable::default(); config.set_max_width(5); config.column(0).set_header("abc"); let input = vec![&[""]]; let expected = "┌───┐\n\ │ + │\n\ ├───┤\n\ │ │\n\ └───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn show_no_content_for_header2() { let mut config = AsciiTable::default(); config.column(0).set_header("abc").set_max_width(1); let input = vec![&[""]]; let expected = "┌───┐\n\ │ + │\n\ ├───┤\n\ │ │\n\ └───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn show_one_character_for_header() { let mut config = AsciiTable::default(); config.set_max_width(6); config.column(0).set_header("abc"); let input = vec![&[""]]; let expected = "┌────┐\n\ │ a+ │\n\ ├────┤\n\ │ │\n\ └────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn show_one_character_for_header2() { let mut config = AsciiTable::default(); config.column(0).set_header("abc").set_max_width(2); let input = vec![&[""]]; let expected = "┌────┐\n\ │ a+ │\n\ ├────┤\n\ │ │\n\ └────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn cube_with_partial_content() { let config = cube_config(); let input: Vec<&[i32]> = vec![&[1, 2, 3], &[4, 5], &[7]]; let expected = "┌───┬───┬───┐\n\ │ a │ b │ c │\n\ ├───┼───┼───┤\n\ │ 1 │ 2 │ 3 │\n\ │ 4 │ 5 │ │\n\ │ 7 │ │ │\n\ └───┴───┴───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn cube_with_partial_content_reversed() { let config = cube_config(); let input: Vec<&[i32]> = vec![&[1], &[4, 5], &[7, 8, 9]]; let expected = "┌───┬───┬───┐\n\ │ a │ b │ c │\n\ ├───┼───┼───┤\n\ │ 1 │ │ │\n\ │ 4 │ 5 │ │\n\ │ 7 │ 8 │ 9 │\n\ └───┴───┴───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn resize_column() { let config = cube_config(); let input = vec![&[1], &[23], &[456]]; let expected = "┌─────┐\n\ │ a │\n\ ├─────┤\n\ │ 1 │\n\ │ 23 │\n\ │ 456 │\n\ └─────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn resize_column_reversed() { let config = cube_config(); let input = vec![&[123], &[45], &[6]]; let expected = "┌─────┐\n\ │ a │\n\ ├─────┤\n\ │ 123 │\n\ │ 45 │\n\ │ 6 │\n\ └─────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn resize_column_via_header() { let mut config = AsciiTable::default(); config.column(0).set_header("foo"); let input = vec![&[1], &[2], &[3]]; let expected = "┌─────┐\n\ │ foo │\n\ ├─────┤\n\ │ 1 │\n\ │ 2 │\n\ │ 3 │\n\ └─────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn partial_header_at_start() { let config = cube_config(); let input = vec![&[1, 2, 3, 0], &[4, 5, 6, 0], &[7, 8, 9, 0]]; let expected = "┌───┬───┬───┬───┐\n\ │ a │ b │ c │ │\n\ ├───┼───┼───┼───┤\n\ │ 1 │ 2 │ 3 │ 0 │\n\ │ 4 │ 5 │ 6 │ 0 │\n\ │ 7 │ 8 │ 9 │ 0 │\n\ └───┴───┴───┴───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn partial_header_at_end() { let mut config = AsciiTable::default(); config.column(2).set_header("c"); let input = vec![&[1, 2, 3], &[4, 5, 6], &[7, 8, 9]]; let expected = "┌───┬───┬───┐\n\ │ │ │ c │\n\ ├───┼───┼───┤\n\ │ 1 │ 2 │ 3 │\n\ │ 4 │ 5 │ 6 │\n\ │ 7 │ 8 │ 9 │\n\ └───┴───┴───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn ignore_unused_header() { let config = cube_config(); let input = vec![&[1], &[2], &[3]]; let expected = "┌───┐\n\ │ a │\n\ ├───┤\n\ │ 1 │\n\ │ 2 │\n\ │ 3 │\n\ └───┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn align_right() { let mut config = AsciiTable::default(); config.column(0).set_header("a").set_align(Right); let input = vec![&[1], &[23], &[456]]; let expected = "┌─────┐\n\ │ a │\n\ ├─────┤\n\ │ 1 │\n\ │ 23 │\n\ │ 456 │\n\ └─────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn align_center() { let mut config = AsciiTable::default(); config.column(0).set_header("a").set_align(Center); let input = vec![&[1], &[23], &[456], &[7890], &[12345]]; let expected = "┌───────┐\n\ │ a │\n\ ├───────┤\n\ │ 1 │\n\ │ 23 │\n\ │ 456 │\n\ │ 7890 │\n\ │ 12345 │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[test] fn mixed_types() { let config = cube_config(); let input: Vec> = vec![ vec![&1, &'2', &"3"], vec![&"4", &5, &'6'], vec![&'7', &"8", &9], ]; let expected = "┌───┬───┬───┐\n\ │ a │ b │ c │\n\ ├───┼───┼───┤\n\ │ 1 │ 2 │ 3 │\n\ │ 4 │ 5 │ 6 │\n\ │ 7 │ 8 │ 9 │\n\ └───┴───┴───┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_zero() { let config = AsciiTable::default(); let input = vec![vec!["\u{1b}[0mHello\u{1b}[0m"]]; let expected = "┌───────┐\n\ │ \u{1b}[0mHello\u{1b}[0m │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_zero_inbetween() { let config = AsciiTable::default(); let input = vec![vec!["He\u{1b}[0ml\u{1b}[0mlo"]]; let expected = "┌───────┐\n\ │ He\u{1b}[0ml\u{1b}[0mlo │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_m5() { let config = AsciiTable::default(); let input = vec![vec!["mmmmm" .color(Color::Blue) .bg_color(Color::Yellow) .bold()]]; let expected = "┌───────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1mmmmmm\u{1b}[0m │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_b5() { let config = AsciiTable::default(); let input = vec![vec!["[[[[[" .color(Color::Blue) .bg_color(Color::Yellow) .bold()]]; let expected = "┌───────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1m[[[[[\u{1b}[0m │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_s5() { let config = AsciiTable::default(); let input = vec![vec![";;;;;" .color(Color::Blue) .bg_color(Color::Yellow) .bold()]]; let expected = "┌───────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1m;;;;;\u{1b}[0m │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_n5() { let config = AsciiTable::default(); let input = vec![vec!["00000" .color(Color::Blue) .bg_color(Color::Yellow) .bold()]]; let expected = "┌───────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1m00000\u{1b}[0m │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_missing_m() { let config = AsciiTable::default(); let input = vec![vec!["\u{1b}[0Hello\u{1b}[0"]]; let expected = "┌───────────┐\n\ │ \u{1b}[0Hello\u{1b}[0 │\n\ └───────────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes() { let config = AsciiTable::default(); let input = vec![ vec!["Hello".color(Color::Blue).bg_color(Color::Yellow).bold()], vec!["Hello".gradient(Color::Red)], ]; let expected = "┌───────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1mHello\u{1b}[0m │\n\ │ \u{1b}[38;2;255;0;0mH\u{1b}[38;2;255;6;0me\u{1b}[38;2;255;13;0ml\u{1b}[38;2;255;19;0ml\u{1b}[38;2;255;26;0mo\u{1b}[0m │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_in_header() { let mut config = AsciiTable::default(); let text = "Hello".color(Color::Blue).bg_color(Color::Yellow).bold(); config.column(0).set_header(text.to_string()); let input = vec![&[""]]; let expected = "┌───────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1mHello\u{1b}[0m │\n\ ├───────┤\n\ │ │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_pad_right() { let config = AsciiTable::default(); let input = vec![ vec!["Hello".color(Color::Blue).bg_color(Color::Yellow).bold()], vec!["H".color(Color::Blue).bg_color(Color::Yellow).bold()], ]; let expected = "┌───────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1mHello\u{1b}[0m │\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1mH \u{1b}[0m │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_pad_left() { let mut config = AsciiTable::default(); config.column(0).set_align(Right); let input = vec![ vec!["Hello".color(Color::Blue).bg_color(Color::Yellow).bold()], vec!["H".color(Color::Blue).bg_color(Color::Yellow).bold()], ]; let expected = "┌───────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1mHello\u{1b}[0m │\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1m H\u{1b}[0m │\n\ └───────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "color_codes")] #[test] fn color_codes_trunc() { let mut config = AsciiTable::default(); config.column(0).set_max_width(2); let input = vec![ vec!["Hello".color(Color::Blue).bg_color(Color::Yellow).bold()], vec!["H".color(Color::Blue).bg_color(Color::Yellow).bold()], ]; let expected = "┌────┐\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1mH+\u{1b}[0m │\n\ │ \u{1b}[38;5;4m\u{1b}[48;5;3;1mH \u{1b}[0m │\n\ └────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "wide_characters")] #[test] fn wide_characters() { let config = AsciiTable::default(); let input = vec![vec!["👩"]]; let expected = "┌────┐\n\ │ 👩 │\n\ └────┘\n"; assert_eq!(expected, config.format(input)); } #[cfg(feature = "wide_characters")] #[test] fn wide_characters2() { let config = AsciiTable::default(); let input = vec![vec!["👩🔬"]]; let expected = "┌──────┐\n\ │ 👩🔬 │\n\ └──────┘\n"; assert_eq!(expected, config.format(input)); }