Repository: Nukesor/comfy-table
Pull request #47 (2021-08-10 17:23:44)

Add a feature flag for tty support
Enabled by default but can be disabled explicitly. 

- Addresses support for wasm-wasi target (GitHub workflow added to validate)
- Addresses unsafe concerns #42 https://github.com/jorgecarleitao/arrow2/pull/251#issuecomment-893373304



Patch:
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<char>,
     pub(crate) alignment: Option<CellAlignment>,
+    #[cfg(feature = "tty")]
     pub(crate) fg: Option<Color>,
+    #[cfg(feature = "tty")]
     pub(crate) bg: Option<Color>,
+    #[cfg(feature = "tty")]
     pub(crate) attributes: Vec<Attribute>,
 }
 
@@ -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<Attribute>) -> 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<u16> {
         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<u16> {
+        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..9b03318 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,12 +124,26 @@ 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<Item = String>,
+    ) -> Vec<String> {
         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()
         }
     }
 
@@ -223,6 +238,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);
 


Test patch:
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/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;