An easy to use library for pretty printing tables of Rust struct
s and enum
s.
To print a list of structs or enums as a table your types should implement the the Tabled
trait or derive with a #[derive(Tabled)]
macro.
```rust use tabled::{Tabled, Table};
struct Language { name: &'static str, designedby: &'static str, inventedyear: usize, }
let languages = vec![ Language{ name: "C", designedby: "Dennis Ritchie", inventedyear: 1972 }, Language{ name: "Rust", designedby: "Graydon Hoare", inventedyear: 2010 }, Language{ name: "Go", designedby: "Rob Pike", inventedyear: 2009 }, ];
let table = Table::new(languages).to_string();
let expected = "+------+----------------+---------------+\n\ | name | designedby | inventedyear |\n\ +------+----------------+---------------+\n\ | C | Dennis Ritchie | 1972 |\n\ +------+----------------+---------------+\n\ | Rust | Graydon Hoare | 2010 |\n\ +------+----------------+---------------+\n\ | Go | Rob Pike | 2009 |\n\ +------+----------------+---------------+\n";
assert_eq!(table, expected); ```
Most of the default types implement the trait out of the box.
rust
use tabled::TableIteratorExt;
let some_numbers = [1, 2, 3];
let table = some_numbers.table();
There are a list of ready to use styles. But a custom style can be created as well.
A style can be used by passing it to .with
method of Table
.
rust
let table = Table::new(&data).with(Style::PSQL);
Bellow rendered a list of pre configured styles.
If you think that there's some valuable style to be added, Please open an issue.
+------+----------------+---------------+
| name | designed_by | invented_year |
+------+----------------+---------------+
| C | Dennis Ritchie | 1972 |
+------+----------------+---------------+
| Rust | Graydon Hoare | 2010 |
+------+----------------+---------------+
| Go | Rob Pike | 2009 |
+------+----------------+---------------+
name | designed_by | invented_year
------+----------------+---------------
C | Dennis Ritchie | 1972
Rust | Graydon Hoare | 2010
Go | Rob Pike | 2009
| name | designed_by | invented_year |
|------+----------------+---------------|
| C | Dennis Ritchie | 1972 |
| Rust | Graydon Hoare | 2010 |
| Go | Rob Pike | 2009 |
┌──────┬────────────────┬───────────────┐
│ name │ designed_by │ invented_year │
├──────┼────────────────┼───────────────┤
│ C │ Dennis Ritchie │ 1972 │
├──────┼────────────────┼───────────────┤
│ Rust │ Graydon Hoare │ 2010 │
├──────┼────────────────┼───────────────┤
│ Go │ Rob Pike │ 2009 │
└──────┴────────────────┴───────────────┘
┌──────┬────────────────┬───────────────┐
│ name │ designed_by │ invented_year │
├──────┼────────────────┼───────────────┤
│ C │ Dennis Ritchie │ 1972 │
│ Rust │ Graydon Hoare │ 2010 │
│ Go │ Rob Pike │ 2009 │
└──────┴────────────────┴───────────────┘
name designed_by invented_year
C Dennis Ritchie 1972
Rust Graydon Hoare 2010
Go Rob Pike 2009
You can modify existing styles to fits your needs.
```rust let style = tabled::Style::NOBORDER .framebottom(Some(Line::short('*', ' '))) .split(Some(Line::short(' ', ' '))) .inner(' ');
let table = Table::new(&data).with(style); ```
You can set a horizontal and vertical alignment for a Header
, Column
, Row
or Full
set of cells.
rust
Table::new(&data)
.with(Modify::new(Full)
.with(Alignment::left())
.with(Alignment::top())
);
The Format
function provides an interface for a modification of cells.
rust
Table::new(&data)
.with(Style::PSQL),
.with(Modify::new(Column(..)).with(Format(|s| format!("<< {} >>", s))))
.with(Modify::new(Row(..1)).with(Format(|s| format!("Head {}", s))));
It's also possible to use functions with signature Fn(&str) -> String
as a formatter.
rust
Table::new(&data)
.with(Style::PSQL),
.with(Modify::new(Column(..)).with(|s: &str| format!("<< {} >>", s)))
.with(Modify::new(Row(..1)).with(str::to_lowercase));
IMPORTANT: you may need to specify type in your lambda otherwise compiler may be disagreed to work :)
There's 2 more Format modifiers. You can find more imformation about theire usage in the documentation.
FormatFrom
- Uses Vec
elements as new content.FormatWithIndex
- Like Format
but with row
and column
index in lambda.The Indent
type provides an interface for a left, right, top and bottom indent of cells.
rust
Table::new(&data).with(Modify::new(Row(1..)).with(Indent::new(1, 1, 0, 2)));
Using MaxWidth
type its possible to set a max width of an object.
While tinkering content we don't forget about its color.
rust
// You can truncate it everything after 10 chars.
Table::new(&data).with(Modify::new(Row(1..)).with(MaxWidth::truncating(10, "...")));
// And you can wrap it content reaching 10 chars.
Table::new(&data).with(Modify::new(Row(1..)).with(MaxWidth::wrapping(10, "...")));
You can rotate table using Rotate
.
Imagine you have a table already. And the output may look like this.
text
┌────┬──────────────┬───────────────────────────┐
│ id │ destribution │ link │
├────┼──────────────┼───────────────────────────┤
│ 0 │ Fedora │ https://getfedora.org/ │
├────┼──────────────┼───────────────────────────┤
│ 2 │ OpenSUSE │ https://www.opensuse.org/ │
├────┼──────────────┼───────────────────────────┤
│ 3 │ Endeavouros │ https://endeavouros.com/ │
└────┴──────────────┴───────────────────────────┘
Now we will add table.with(Rotate::Left)
and the output will be;
text
┌──────────────┬────────────────────────┬───────────────────────────┬──────────────────────────┐
│ link │ https://getfedora.org/ │ https://www.opensuse.org/ │ https://endeavouros.com/ │
├──────────────┼────────────────────────┼───────────────────────────┼──────────────────────────┤
│ destribution │ Fedora │ OpenSUSE │ Endeavouros │
├──────────────┼────────────────────────┼───────────────────────────┼──────────────────────────┤
│ id │ 0 │ 2 │ 3 │
└──────────────┴────────────────────────┴───────────────────────────┴──────────────────────────┘
You can remove certain rows or columns from the table.
rust
Table::new(&data)
.with(Disable::Row(..1))
.with(Disable::Column(3..4));
You can add a Header
and Footer
to display some information.
By the way you can even add such line by using Panel
rust
Table::new(&data)
.with(Header("Tabled Name"))
.with(Footer(format!("{} elements", data.len())))
A look will differ from a style you choose. But it's how it may look like.
text
┌────────────────────────────────────────────────────────────┐
│ Tabled Name │
├────────────────────────────────────────────────────────────┤
...
├───────┼──────────────┼─────────┼───────────────────────────┤
│ 3 elements │
└────────────────────────────────────────────────────────────┘
You can concatanate 2 tables using Concat
.
It will stick 2 tables together either vertically or horizontally.
```rust let t1: Table = ...; let t2: Table = ...;
let t3: Table = t1.with(Concat::vertical(t2)); ```
To be able to use a Tabled
macros each field must implement std::fmt::Display
otherwise it will not work.
The following example will cause a error.
```rust,compile_fail use tabled::Tabled;
struct SomeType { field1: SomeOtherType, }
struct SomeOtherType; ```
You can use a #[header("")]
attribute to override a column name.
```rust
struct Person { #[header("Name")] firstname: &'static str, #[header("Surname")] lastname: &'static str, } ```
You can mark filds as hidden in which case they fill be ignored and not be present on a sheet.
A similar affect could be achived by the means of a Disable
setting.
rust
struct Person {
#[header(hidden = true)]
id: u8,
#[header("field 2", hidden)]
number: &'static str,
name: &'static str,
}
#[derive(Tabled)]
is possible only when all fields implement a Display
trait.
However, this may be often not the case for example when a field uses the Option
type.
There's 2 common ways how to solve this:
Tabled
trait manually for a type.Option
to something like DisplayedOption<T>(Option<T>)
and implement a Display trait for it.Or to use an attribute #[field(display_with = "func")]
for the field. To use it you must provide a function name in a display_with
parameter.
```rust
fn display_option(o: &Option
pub struct MyRecord {
pub id: i64,
#[field(displaywith="displayoption")]
pub valid: Option
It's possible to inline internal data if it implements Tabled
trait.
Use #[header(inline)]
or #[header(inline("prefix>>"))]
.
The string argument is a prefix which will be used for all inlined elements.
```rust #[derive(Tabled)] struct Person { id: u8, name: &'static str, #[header(inline)] ed: Education, }
struct Education { uni: &'static str, graduated: bool, } ```
And it works for enums as well.
```rust
enum Vehicle { #[header(inline("Auto::"))] Auto { model: &'static str, engine: &'static str, }, #[header(inline)] Bikecycle(#[header("name")] &'static str, #[header(inline)] Bike), }
struct Bike { brand: &'static str, price: f32, } ```
The library doesn't bind you in usage of any color library but to be able to work corectly with color input you should provide a --features color
.
rust
Table::new(&data)
.with(Style::PSQL)
.with(Modify::new(Column(..1)).with(Format(|s| s.red().to_string())))
.with(Modify::new(Column(1..2)).with(Format(|s| s.blue().to_string())))
.with(Modify::new(Column(2..)).with(Format(|s| s.green().to_string())));
You also can combine objets which implements Tabled
by means of tuples, you will get a combined columns of them.
```rust use tabled::{Tabled, Table, Style};
enum Domain { Security, Embeded, Frontend, Unknown, }
struct Developer(#[header("name")] &'static str);
let data = vec![ (Developer("Terri Kshlerin"), Domain::Embeded), (Developer("Catalina Dicki"), Domain::Security), (Developer("Jennie Schmeler"), Domain::Frontend), (Developer("Maxim Zhiburt"), Domain::Unknown), ];
let table = Table::new(data).with(Style::PSQL).to_string();
assert_eq!( table, concat!( " name | Security | Embeded | Frontend | Unknown \n", "-----------------+----------+---------+----------+---------\n", " Terri Kshlerin | | + | | \n", " Catalina Dicki | + | | | \n", " Jennie Schmeler | | | + | \n", " Maxim Zhiburt | | | | + \n" ) ); ```
You can peak your target for settings using and
and not
methods for an object.
rust
Full.not(Row(..1)) // peak all cells except header
Head.and(Column(..1)).not(Cell(0, 0)) // peak a header and first column except a (0, 0) cell
Tabled
supports not only Table view!
You can use ExpanedDisplay
if your data structure has a lot of fields.
Here's an example.
```rust use tabled::{display::ExpandedDisplay, Tabled};
struct Distribution { name: &'static str, isactive: bool, iscool: bool, }
fn main() { let data = [ Distribution { name: "Manjaro", iscool: true, isactive: true, }, Distribution { name: "Debian", iscool: true, isactive: true, }, Distribution { name: "Debian", iscool: true, isactive: true, }, ];
let table = ExpandedDisplay::new(&data);
println!("{}", table);
} ```
You'll see the following.
text
-[ RECORD 0 ]------
name | Manjaro
is_active | true
is_cool | true
-[ RECORD 1 ]------
name | Debian
is_active | true
is_cool | true
-[ RECORD 2 ]------
name | Debian
is_active | true
is_cool | true
By default tabled
doesn't handle ANSI escape codes.
By default such things as hyperlinks, blinking and others things which can be achived via ANSI codes might not work correctly.
tabled
support it by setting a color
feature.
toml
tabled = { version = "*", features = ["color"] }
The library support emojies out of the box but be aware that some of the terminals and editors may not render them as you would expect.
Let's add emojies to an example from a Usage section.
rust
let languages = vec![
Language {
name: "C 💕",
designed_by: "Dennis Ritchie",
invented_year: 1972,
},
Language {
name: "Rust 👍",
designed_by: "Graydon Hoare",
invented_year: 2010,
},
Language {
name: "Go 🧋",
designed_by: "Rob Pike",
invented_year: 2009,
},
];
The resultant table will look like the following.
As you can see Github triks a bit a return table, but GNOME terminal
and Alacritty
terminal handles it correctly.
rust
+---------+----------------+---------------+
| name | designed_by | invented_year |
+---------+----------------+---------------+
| C 💕 | Dennis Ritchie | 1972 |
+---------+----------------+---------------+
| Rust 👍 | Graydon Hoare | 2010 |
+---------+----------------+---------------+
| Go 🧋 | Rob Pike | 2009 |
+---------+----------------+---------------+