Repository: zhiburt/tabled (Rust)

Issue:
Table borders not rendered for empty JSON values under "Style::modern()" and "collapsed" mode
Hello, this crate has been really helpful in a REPL program I've been writing, thank you.

I'm using `json_to_table` (v 0.2.0 from crates.io). I'm rendering JSON objects using the "Modern" table style, and ran into the following issue:

```rust
use json_to_table::json_to_table;
use serde_json::json;
let value = json!(
    {
        "field1": "Value",
        "field2": 3,
        "field3" : {}
    }
);
let mut table = json_to_table(&value);
table.set_style(tabled::style::Style::modern());
println!("{}", table.to_string());

let mut table_collapsed = table.clone();
println!("{}", table_collapsed.collapse().to_string());
```

The first table renders correctly, but the borders for the second table aren't rendered for the missing "field3":

![Screenshot from 2022-11-10 09-03-14](https://user-images.githubusercontent.com/24863708/201086945-49118f03-e3db-4b47-9709-ca7ea63f5d92.png)

Best,


Discussion:
Hi @limads

Thank you for report and the example,
And sorry for inconveniences.

I believe it must be fixed with `0.3.0`.
Could you try it out?

There was no inconvenience at all. Can confirm the problem has been solved, thank you!



Patch:
diff --git a/json_to_table/Cargo.toml b/json_to_table/Cargo.toml
index 4c60d3e7..3af41c30 100644
--- a/json_to_table/Cargo.toml
+++ b/json_to_table/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "json_to_table"
-version = "0.2.0"
+version = "0.3.0"
 edition = "2021"
 authors = ["Maxim Zhiburt <zhiburt@gmail.com>"]
 repository = "https://github.com/zhiburt/tabled"
diff --git a/json_to_table/src/table.rs b/json_to_table/src/table.rs
index b5ae10dd..f1dc4903 100644
--- a/json_to_table/src/table.rs
+++ b/json_to_table/src/table.rs
@@ -348,12 +348,30 @@ mod json_to_table {
                 table
             }
             Value::Object(obj) => {
+                if obj.is_empty() {
+                    // a corner case where the object must behave as empty string
+
+                    return json_to_table_r(
+                        &Value::String(String::new()),
+                        config,
+                        row,
+                        column,
+                        is_last,
+                        is_prev_row_last,
+                        is_in_list,
+                        change_key_split,
+                        used_splits,
+                        width,
+                    );
+                }
+
                 let map_length = obj.len();
                 let max_keys_width = obj
                     .iter()
                     .map(|(key, _)| col![key].with(NoRightBorders).total_width())
                     .max()
                     .unwrap_or(0);
+
                 let width = match width {
                     Some(width) => width,
                     None => {
@@ -390,6 +408,7 @@ mod json_to_table {
                         width + max_keys_width
                     }
                 };
+
                 let mut builder = Builder::new();
                 let mut iter = obj.iter().enumerate().peekable();
                 while let Some((i, value)) = iter.next() {
@@ -427,6 +446,9 @@ mod json_to_table {
 
                     let is_last = is_last && i + 1 == map_length;
                     let width = width - max_keys_width;
+
+                    println!("{:?}", value);
+
                     let mut value = json_to_table_r(
                         value,
                         config,
@@ -439,6 +461,7 @@ mod json_to_table {
                         &intersections,
                         Some(width),
                     );
+
                     {
                         value.with(TopLeftChangeSplit);
 
@@ -525,11 +548,29 @@ mod json_to_table {
 
                     builder.add_record([key.to_string(), value.to_string()]);
                 }
+
                 let mut table = builder.build();
                 table.with(Style::empty()).with(Padding::zero());
                 table
             }
             Value::Array(list) => {
+                if list.is_empty() {
+                    // a corner case where the list must behave as empty string
+
+                    return json_to_table_r(
+                        &Value::String(String::new()),
+                        config,
+                        row,
+                        column,
+                        is_last,
+                        is_prev_row_last,
+                        is_in_list,
+                        change_key_split,
+                        used_splits,
+                        width,
+                    );
+                }
+
                 let width = match width {
                     Some(width) => width,
                     None => {
diff --git a/papergrid/src/grid.rs b/papergrid/src/grid.rs
index 627a9666..4ef40fc5 100644
--- a/papergrid/src/grid.rs
+++ b/papergrid/src/grid.rs
@@ -692,7 +692,7 @@ where
     let formatting = *cfg.get_formatting(pos.into());
     if formatting.vertical_trim {
         cell_height -=
-            count_empty_lines_at_start(&records, pos) + count_empty_lines_at_end(&records, pos);
+            count_empty_lines_at_start(records, pos) + count_empty_lines_at_end(records, pos);
     }
 
     if cell_height > height {
@@ -730,7 +730,7 @@ where
     }
 
     if formatting.vertical_trim {
-        let empty_lines = count_empty_lines_at_start(&records, pos);
+        let empty_lines = count_empty_lines_at_start(records, pos);
         index += empty_lines;
 
         if index > records.count_lines(pos) {


Test patch:
diff --git a/json_to_table/tests/test_table.rs b/json_to_table/tests/test_table.rs
index eba3c2ba..559ce89c 100644
--- a/json_to_table/tests/test_table.rs
+++ b/json_to_table/tests/test_table.rs
@@ -708,3 +708,323 @@ fn array_split_with_inner_array_2_test() {
         )
     );
 }
+
+#[test]
+fn test_map_empty_entity_collapsed_0() {
+    let value = json!(
+        {
+            "field1" : {},
+            "field2": "Value",
+            "field3" : {},
+            "field4": 3,
+            "field5" : {}
+        }
+    );
+
+    let table = json_to_table(&value)
+        .set_style(Style::modern())
+        .collapse()
+        .to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌────────┬───────┐\n",
+            "│ field1 │       │\n",
+            "├────────┼───────┤\n",
+            "│ field2 │ Value │\n",
+            "├────────┼───────┤\n",
+            "│ field3 │       │\n",
+            "├────────┼───────┤\n",
+            "│ field4 │ 3     │\n",
+            "├────────┼───────┤\n",
+            "│ field5 │       │\n",
+            "└────────┴───────┘",
+        )
+    );
+}
+
+#[test]
+fn test_map_empty_entity_collapsed_1() {
+    let value = json!(
+        {
+            "field1" : [],
+            "field2": "Value",
+            "field3" : [],
+            "field4": 3,
+            "field5" : []
+        }
+    );
+
+    let table = json_to_table(&value)
+        .set_style(Style::modern())
+        .collapse()
+        .to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌────────┬───────┐\n",
+            "│ field1 │       │\n",
+            "├────────┼───────┤\n",
+            "│ field2 │ Value │\n",
+            "├────────┼───────┤\n",
+            "│ field3 │       │\n",
+            "├────────┼───────┤\n",
+            "│ field4 │ 3     │\n",
+            "├────────┼───────┤\n",
+            "│ field5 │       │\n",
+            "└────────┴───────┘",
+        )
+    );
+}
+
+#[test]
+fn test_map_empty_entity_collapsed_2() {
+    let value = json!(
+        {
+            "key0": {},
+            "field1" : {
+                "key1": "value1",
+                "key2": []
+            },
+            "field2": "Value",
+            "field3" : [],
+            "field4": [
+                {},
+                "123",
+                {},
+                "sadas",
+                {}
+            ],
+            "field5" : []
+        }
+    );
+
+    let table = json_to_table(&value)
+        .set_style(Style::modern())
+        .collapse()
+        .to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌────────┬──────┬────────┐\n",
+            "│ field1 │ key1 │ value1 │\n",
+            "│        ├──────┼────────┤\n",
+            "│        │ key2 │        │\n",
+            "├────────┼──────┴────────┤\n",
+            "│ field2 │ Value         │\n",
+            "├────────┼───────────────┤\n",
+            "│ field3 │               │\n",
+            "├────────┼───────────────┤\n",
+            "│ field4 │               │\n",
+            "│        ├───────────────┤\n",
+            "│        │ 123           │\n",
+            "│        ├───────────────┤\n",
+            "│        │               │\n",
+            "│        ├───────────────┤\n",
+            "│        │ sadas         │\n",
+            "│        ├───────────────┤\n",
+            "│        │               │\n",
+            "├────────┼───────────────┤\n",
+            "│ field5 │               │\n",
+            "├────────┼───────────────┤\n",
+            "│ key0   │               │\n",
+            "└────────┴───────────────┘",
+        )
+    );
+}
+
+#[test]
+fn test_list_empty_entity_collapsed_0() {
+    let value = json!([{}, "field1", {}, "field2", {}]);
+
+    let table = json_to_table(&value)
+        .set_style(Style::modern())
+        .collapse()
+        .to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌────────┐\n",
+            "│        │\n",
+            "├────────┤\n",
+            "│ field1 │\n",
+            "├────────┤\n",
+            "│        │\n",
+            "├────────┤\n",
+            "│ field2 │\n",
+            "├────────┤\n",
+            "│        │\n",
+            "└────────┘",
+        )
+    );
+}
+
+#[test]
+fn test_list_empty_entity_collapsed_1() {
+    let value = json!([{}, {}, {}]);
+
+    let table = json_to_table(&value)
+        .set_style(Style::modern())
+        .collapse()
+        .to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌──┐\n",
+            "│  │\n",
+            "├──┤\n",
+            "│  │\n",
+            "├──┤\n",
+            "│  │\n",
+            "└──┘",
+        )
+    );
+}
+
+#[test]
+fn test_list_empty_entity_collapsed_2() {
+    let value = json!([[], [], []]);
+
+    let table = json_to_table(&value)
+        .set_style(Style::modern())
+        .collapse()
+        .to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌──┐\n",
+            "│  │\n",
+            "├──┤\n",
+            "│  │\n",
+            "├──┤\n",
+            "│  │\n",
+            "└──┘",
+        )
+    );
+}
+
+#[test]
+fn test_map_empty_entity_plain_0() {
+    let value = json!(
+        {
+            "field1" : {},
+            "field2": "Value",
+            "field3" : {},
+            "field4": 3,
+            "field5" : {}
+        }
+    );
+
+    let table = json_to_table(&value).set_style(Style::modern()).to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌────────┬─────────┐\n",
+            "│ field1 │         │\n",
+            "├────────┼─────────┤\n",
+            "│ field2 │  Value  │\n",
+            "├────────┼─────────┤\n",
+            "│ field3 │         │\n",
+            "├────────┼─────────┤\n",
+            "│ field4 │  3      │\n",
+            "├────────┼─────────┤\n",
+            "│ field5 │         │\n",
+            "└────────┴─────────┘",
+        )
+    );
+}
+
+#[test]
+fn test_list_empty_entity_plain_0() {
+    let value = json!([{}, "field1", {}, "field2", {}]);
+
+    let table = json_to_table(&value).set_style(Style::modern()).to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌──────────┐\n",
+            "│          │\n",
+            "├──────────┤\n",
+            "│  field1  │\n",
+            "├──────────┤\n",
+            "│          │\n",
+            "├──────────┤\n",
+            "│  field2  │\n",
+            "├──────────┤\n",
+            "│          │\n",
+            "└──────────┘",
+        )
+    );
+}
+
+#[test]
+fn test_list_empty_entity_plain_1() {
+    let value = json!([[], "field1", [], "field2", []]);
+
+    let table = json_to_table(&value).set_style(Style::modern()).to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌──────────┐\n",
+            "│          │\n",
+            "├──────────┤\n",
+            "│  field1  │\n",
+            "├──────────┤\n",
+            "│          │\n",
+            "├──────────┤\n",
+            "│  field2  │\n",
+            "├──────────┤\n",
+            "│          │\n",
+            "└──────────┘",
+        )
+    );
+}
+
+#[test]
+fn test_list_empty_entity_plain_2() {
+    let value = json!([{}, {}, {}]);
+
+    let table = json_to_table(&value).set_style(Style::modern()).to_string();
+
+    println!("{}", table);
+
+    assert_eq!(
+        table,
+        concat!(
+            "┌──┐\n",
+            "│  │\n",
+            "├──┤\n",
+            "│  │\n",
+            "├──┤\n",
+            "│  │\n",
+            "└──┘",
+        )
+    );
+}
diff --git a/json_to_table/tests/test_types.rs b/json_to_table/tests/test_types.rs
index df5d7384..e3ea054e 100644
--- a/json_to_table/tests/test_types.rs
+++ b/json_to_table/tests/test_types.rs
@@ -178,6 +178,13 @@ mod squashed {
             .to_string();
 
         assert_eq!(table, table_squashed);
+
+        let table = json_to_table(&json!(""))
+            .set_style(Style::modern())
+            .collapse()
+            .to_string();
+
+        assert_eq!(table, "┌──┐\n│  │\n└──┘");
     }
 
     #[test]
@@ -285,6 +292,14 @@ mod squashed {
                 "└─────────────┘",
             )
         );
+
+        let value = json!([]);
+        let table = json_to_table(&value)
+            .set_style(Style::modern())
+            .collapse()
+            .to_string();
+
+        assert_eq!(table, "┌──┐\n│  │\n└──┘");
     }
 
     #[test]
@@ -326,5 +341,13 @@ mod squashed {
                 "└─────────┴────────┴─────────────┘",
             )
         );
+
+        let value = json!({});
+        let table = json_to_table(&value)
+            .set_style(Style::modern())
+            .collapse()
+            .to_string();
+
+        assert_eq!(table, "┌──┐\n│  │\n└──┘");
     }
 }