From 58306c0a58fcf522ebd94f2ee05dd8b809c29985 Mon Sep 17 00:00:00 2001 From: roee88 Date: Tue, 10 Aug 2021 19:30:16 +0300 Subject: [PATCH 1/2] Add a feature flag for tty support Enabled by default but can be disabled explicitly. Address support for wasm-wasi and unsafe concerns. Signed-off-by: roee88 --- .github/workflows/test.yml | 24 +++++++++++++++++++++ Cargo.toml | 15 ++++++++++++- benches/build_tables.rs | 27 ++++++++++++++++++++++++ examples/readme_table_no_tty.rs | 29 ++++++++++++++++++++++++++ src/cell.rs | 11 ++++++++++ src/style/mod.rs | 2 ++ src/table.rs | 19 +++++++++++++++++ src/utils/formatting/content_format.rs | 8 ++++++- tests/all/mod.rs | 2 ++ 9 files changed, 135 insertions(+), 2 deletions(-) create mode 100644 examples/readme_table_no_tty.rs diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e6baa9f..5f964fa 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -46,3 +46,27 @@ jobs: uses: actions-rs/cargo@v1 with: command: test + wasm: + name: Check wasm32-wasi support with ${{ matrix.toolchain }} toolchain + runs-on: ubuntu-latest + strategy: + matrix: + toolchain: [stable, nightly] + + steps: + - name: Checkout code + uses: actions/checkout@v2 + + - name: Setup Rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + target: wasm32-wasi + toolchain: ${{ matrix.toolchain }} + override: true + + - name: cargo build + uses: actions-rs/cargo@v1 + with: + command: build + args: --release --no-default-features --target wasm32-wasi diff --git a/Cargo.toml b/Cargo.toml index 70f78a0..2642e93 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,11 +15,16 @@ edition = "2018" maintenance = { status = "actively-developed" } [dependencies] -crossterm = "0.20" +crossterm = {version = "0.20", optional = true } strum = "0.21" strum_macros = "0.21" unicode-width = "0.1" + +[features] +default = ["tty"] +tty = ["crossterm"] + [dev-dependencies] pretty_assertions = "0.7" doc-comment = "0.3" @@ -29,3 +34,11 @@ criterion = "0.3" [[bench]] name = "build_tables" harness = false + +[[example]] +name = "no_tty" +path = "examples/readme_table_no_tty.rs" + +[[example]] +name = "readme_table" +path = "examples/readme_table.rs" diff --git a/benches/build_tables.rs b/benches/build_tables.rs index a3b2707..976bc11 100644 --- a/benches/build_tables.rs +++ b/benches/build_tables.rs @@ -6,6 +6,7 @@ use comfy_table::Width::*; use comfy_table::*; /// Build the readme table +#[cfg(feature = "tty")] fn build_readme_table() { let mut table = Table::new(); table.load_preset(UTF8_FULL) @@ -37,6 +38,32 @@ fn build_readme_table() { let _ = table.lines(); } +#[cfg(not(feature = "tty"))] +fn build_readme_table() { + let mut table = Table::new(); + table.load_preset(UTF8_FULL) + .set_content_arrangement(ContentArrangement::Dynamic) + .set_table_width(80) + .set_header(vec![ + Cell::new("Header1"), + Cell::new("Header2"), + Cell::new("Header3"), + ]) + .add_row(vec![ + Cell::new("This is a bold text"), + Cell::new("This is a green text"), + Cell::new("This one has black background"), + ]) + .add_row(vec![ + Cell::new("Blinky boi"), + Cell::new("This table's content is dynamically arranged. The table is exactly 80 characters wide.\nHere comes a reallylongwordthatshoulddynamicallywrap"), + Cell::new("COMBINE ALL THE THINGS"), + ]); + + // Build the table. + let _ = table.lines(); +} + /// Create a dynamic 10x10 Table with width 400 and unevenly distributed content. /// On top of that, most of the columns have some kind of constraint. fn build_huge_table() { diff --git a/examples/readme_table_no_tty.rs b/examples/readme_table_no_tty.rs new file mode 100644 index 0000000..41ca53f --- /dev/null +++ b/examples/readme_table_no_tty.rs @@ -0,0 +1,29 @@ +use comfy_table::presets::UTF8_FULL; +use comfy_table::*; + +// This example works even with the `tty` feature disabled +// You can try it out with `cargo run --example no_tty --no-default-features` + +fn main() { + let mut table = Table::new(); + table.load_preset(UTF8_FULL) + .set_content_arrangement(ContentArrangement::Dynamic) + .set_table_width(80) + .set_header(vec![ + Cell::new("Header1"), + Cell::new("Header2"), + Cell::new("Header3"), + ]) + .add_row(vec![ + Cell::new("No bold text without tty"), + Cell::new("No colored text without tty"), + Cell::new("No custom background without tty"), + ]) + .add_row(vec![ + Cell::new("Blinky boi"), + Cell::new("This table's content is dynamically arranged. The table is exactly 80 characters wide.\nHere comes a reallylongwordthatshoulddynamicallywrap"), + Cell::new("Done"), + ]); + + println!("{}", table); +} diff --git a/src/cell.rs b/src/cell.rs index e3d93e1..39b8f04 100644 --- a/src/cell.rs +++ b/src/cell.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "tty")] use crossterm::style::{Attribute, Color}; use crate::style::CellAlignment; @@ -13,8 +14,11 @@ pub struct Cell { /// The default is ` `. pub(crate) delimiter: Option, pub(crate) alignment: Option, + #[cfg(feature = "tty")] pub(crate) fg: Option, + #[cfg(feature = "tty")] pub(crate) bg: Option, + #[cfg(feature = "tty")] pub(crate) attributes: Vec, } @@ -29,8 +33,11 @@ impl Cell { .collect(), delimiter: None, alignment: None, + #[cfg(feature = "tty")] fg: None, + #[cfg(feature = "tty")] bg: None, + #[cfg(feature = "tty")] attributes: Vec::new(), } } @@ -77,6 +84,7 @@ impl Cell { /// let mut cell = Cell::new("Some content") /// .fg(Color::Red); /// ``` + #[cfg(feature = "tty")] pub fn fg(mut self, color: Color) -> Self { self.fg = Some(color); @@ -94,6 +102,7 @@ impl Cell { /// let mut cell = Cell::new("Some content") /// .bg(Color::Red); /// ``` + #[cfg(feature = "tty")] pub fn bg(mut self, color: Color) -> Self { self.bg = Some(color); @@ -112,6 +121,7 @@ impl Cell { /// let mut cell = Cell::new("Some content") /// .add_attribute(Attribute::Bold); /// ``` + #[cfg(feature = "tty")] pub fn add_attribute(mut self, attribute: Attribute) -> Self { self.attributes.push(attribute); @@ -119,6 +129,7 @@ impl Cell { } /// Same as add_attribute, but you can pass a vector of [Attributes](Attribute) + #[cfg(feature = "tty")] pub fn add_attributes(mut self, mut attribute: Vec) -> Self { self.attributes.append(&mut attribute); diff --git a/src/style/mod.rs b/src/style/mod.rs index 9caeb48..6976cd1 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -17,6 +17,8 @@ pub use column::{ColumnConstraint, Width}; pub use table::{ContentArrangement, TableComponent}; /// Attributes used for styling cell content. Reexport of crossterm's [Attributes](crossterm::style::Attribute) enum. +#[cfg(feature = "tty")] pub use crossterm::style::Attribute; /// Colors used for styling cell content. Reexport of crossterm's [Color](crossterm::style::Color) enum. +#[cfg(feature = "tty")] pub use crossterm::style::Color; diff --git a/src/table.rs b/src/table.rs index 3e90b6c..aed3f0a 100644 --- a/src/table.rs +++ b/src/table.rs @@ -4,7 +4,9 @@ use std::fmt; use std::iter::IntoIterator; use std::slice::{Iter, IterMut}; +#[cfg(feature = "tty")] use crossterm::terminal::size; +#[cfg(feature = "tty")] use crossterm::tty::IsTty; use strum::IntoEnumIterator; @@ -137,6 +139,7 @@ impl Table { /// /// If neither is not possible, `None` will be returned.\ /// This implies that both the [Dynamic](ContentArrangement::Dynamic) mode and the [Percentage](crate::style::ColumnConstraint::Percentage) constraint won't work. + #[cfg(feature = "tty")] pub fn get_table_width(&self) -> Option { if let Some(width) = self.table_width { Some(width) @@ -151,6 +154,15 @@ impl Table { } } + #[cfg(not(feature = "tty"))] + pub fn get_table_width(&self) -> Option { + if let Some(width) = self.table_width { + Some(width) + } else { + None + } + } + /// Specify how Comfy Table should arrange the content in your table. /// /// ``` @@ -196,6 +208,7 @@ impl Table { /// /// This function respects the [Table::force_no_tty] function.\ /// Otherwise we try to determine, if we are on a tty. + #[cfg(feature = "tty")] pub fn is_tty(&self) -> bool { if self.no_tty { return false; @@ -204,6 +217,11 @@ impl Table { ::std::io::stdout().is_tty() } + #[cfg(not(feature = "tty"))] + pub fn is_tty(&self) -> bool { + false + } + /// Enforce terminal styling. /// /// Only useful if you forcefully disabled tty, but still want those fancy terminal styles. @@ -215,6 +233,7 @@ impl Table { /// table.force_no_tty() /// .enforce_styling(); /// ``` + #[cfg(feature = "tty")] pub fn enforce_styling(&mut self) -> &mut Self { self.enforce_styling = true; diff --git a/src/utils/formatting/content_format.rs b/src/utils/formatting/content_format.rs index a0bea5d..9d60bbe 100644 --- a/src/utils/formatting/content_format.rs +++ b/src/utils/formatting/content_format.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "tty")] use crossterm::style::{style, Stylize}; use unicode_width::UnicodeWidthStr; @@ -123,13 +124,17 @@ pub fn format_row( .iter() .map(|line| align_line(line.to_string(), info, cell)); - // Style the cell if necessary. + // Style the cell if necessary + #[cfg(feature = "tty")] if table.should_style() { let cell_lines = cell_lines.map(|line| style_line(line, cell)); temp_row_content.push(cell_lines.collect()); } else { temp_row_content.push(cell_lines.collect()); } + + #[cfg(not(feature = "tty"))] + temp_row_content.push(cell_lines.collect()); } // Right now, we have a different structure than desired. @@ -223,6 +228,7 @@ fn pad_line(line: String, info: &ColumnDisplayInfo) -> String { padded_line } +#[cfg(feature = "tty")] fn style_line(line: String, cell: &Cell) -> String { let mut content = style(line); diff --git a/tests/all/mod.rs b/tests/all/mod.rs index f1575ba..a1029f8 100644 --- a/tests/all/mod.rs +++ b/tests/all/mod.rs @@ -1,4 +1,5 @@ mod alignment_test; +#[cfg(feature = "tty")] mod combined_test; mod constraints_test; mod content_arrangement_test; @@ -9,5 +10,6 @@ mod padding_test; mod presets_test; mod property_test; mod simple_test; +#[cfg(feature = "tty")] mod styling_test; mod utf_8_characters; From 39369ec927cf41ba1a6eeeb2bd2e73607d6e21af Mon Sep 17 00:00:00 2001 From: roee88 Date: Wed, 11 Aug 2021 15:07:06 +0300 Subject: [PATCH 2/2] extracted tty styling to function Signed-off-by: roee88 --- src/utils/formatting/content_format.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/utils/formatting/content_format.rs b/src/utils/formatting/content_format.rs index 9d60bbe..9b03318 100644 --- a/src/utils/formatting/content_format.rs +++ b/src/utils/formatting/content_format.rs @@ -124,17 +124,27 @@ pub fn format_row( .iter() .map(|line| align_line(line.to_string(), info, cell)); - // Style the cell if necessary + // Apply tty styling for this cell. #[cfg(feature = "tty")] + let cell_lines = apply_tty_styling(table, cell, cell_lines).into_iter(); + + temp_row_content.push(cell_lines.collect()); + } + + #[cfg(feature = "tty")] + /// A small wrapper around the top-level cell styling logic. It's only used to have a clear + /// separation of our tty styling logic for the `tty` feature flag. + fn apply_tty_styling( + table: &Table, + cell: &Cell, + cell_lines: impl Iterator, + ) -> Vec { if table.should_style() { let cell_lines = cell_lines.map(|line| style_line(line, cell)); - temp_row_content.push(cell_lines.collect()); + cell_lines.collect() } else { - temp_row_content.push(cell_lines.collect()); + cell_lines.collect() } - - #[cfg(not(feature = "tty"))] - temp_row_content.push(cell_lines.collect()); } // Right now, we have a different structure than desired.