Skip to content

Commit 0b2b3f1

Browse files
authored
Add display trait impl for table struct (#20)
1 parent 9423899 commit 0b2b3f1

File tree

9 files changed

+359
-287
lines changed

9 files changed

+359
-287
lines changed

cli-table-derive/src/lib.rs

Lines changed: 2 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -2,154 +2,9 @@
22
#![cfg_attr(not(any(docsrs, feature = "doc")), forbid(unstable_features))]
33
#![deny(missing_docs)]
44
#![cfg_attr(any(docsrs, feature = "doc"), feature(doc_cfg))]
5-
//! Rust crate for printing tables on command line.
5+
//! Derive macros for `cli-table` crate.
66
//!
7-
//! # Usage
8-
//!
9-
//! Add `cli-table` in your `Cargo.toms`'s `dependencies` section
10-
//!
11-
//! ```toml
12-
//! [dependencies]
13-
//! cli-table = "0.4"
14-
//! ```
15-
//!
16-
//! ## Simple usage
17-
//!
18-
//! ```rust,ignore
19-
//! use cli_table::{format::Justify, print_stdout, Cell, Style, Table};
20-
//!
21-
//! let table = vec![
22-
//! vec!["Tom".cell(), 10.cell().justify(Justify::Right)],
23-
//! vec!["Jerry".cell(), 15.cell().justify(Justify::Right)],
24-
//! vec!["Scooby Doo".cell(), 20.cell().justify(Justify::Right)],
25-
//! ]
26-
//! .table()
27-
//! .title(vec![
28-
//! "Name".cell().bold(true),
29-
//! "Age (in years)".cell().bold(true),
30-
//! ])
31-
//! .bold(true);
32-
//!
33-
//! assert!(print_stdout(table).is_ok());
34-
//! ```
35-
//!
36-
//! Below is the output of the table we created just now:
37-
//!
38-
//! ```markdown
39-
//! +------------+----------------+
40-
//! | Name | Age (in years) | <-- This row and all the borders/separators
41-
//! +------------+----------------+ will appear in bold
42-
//! | Tom | 10 |
43-
//! +------------+----------------+
44-
//! | Jerry | 15 |
45-
//! +------------+----------------+
46-
//! | Scooby Doo | 25 |
47-
//! +------------+----------------+
48-
//! ```
49-
//!
50-
//! ## Derive macro
51-
//!
52-
//! `#[derive(Table)]` can also be used to print a `Vec` or slice of `struct`s as table.
53-
//!
54-
//! ```rust,ignore
55-
//! use cli_table::{format::Justify, print_stdout, Table, WithTitle};
56-
//!
57-
//! #[derive(Table)]
58-
//! struct User {
59-
//! #[table(title = "ID", justify = "Justify::Right")]
60-
//! id: u64,
61-
//! #[table(title = "First Name")]
62-
//! first_name: &'static str,
63-
//! #[table(title = "Last Name")]
64-
//! last_name: &'static str,
65-
//! }
66-
//!
67-
//! let users = vec![
68-
//! User {
69-
//! id: 1,
70-
//! first_name: "Scooby",
71-
//! last_name: "Doo",
72-
//! },
73-
//! User {
74-
//! id: 2,
75-
//! first_name: "John",
76-
//! last_name: "Cena",
77-
//! },
78-
//! ];
79-
//!
80-
//! assert!(print_stdout(users.with_title()).is_ok());
81-
//! ```
82-
//!
83-
//! Below is the output of the table we created using derive macro:
84-
//!
85-
//! ```markdown
86-
//! +----+------------+-----------+
87-
//! | ID | First Name | Last Name | <-- This row will appear in bold
88-
//! +----+------------+-----------+
89-
//! | 1 | Scooby | Doo |
90-
//! +----+------------+-----------+
91-
//! | 2 | John | Cena |
92-
//! +----+------------+-----------+
93-
//! ```
94-
//!
95-
//! ### Field attributes
96-
//!
97-
//! - `title` | `name`: Used to specify title of a column. Usage: `#[table(title = "Title")]`
98-
//! - `justify`: Used to horizontally justify the contents of a column. Usage: `#[table(justify = "Justify::Right")]`
99-
//! - `align`: Used to vertically align the contents of a column. Usage: `#[table(align = "Align::Top")]`
100-
//! - `color`: Used to specify color of contents of a column. Usage: `#[table(color = "Color::Red")]`
101-
//! - `bold`: Used to specify boldness of contents of a column. Usage: `#[table(bold)]`
102-
//! - `order`: Used to order columns in a table while printing. Usage: `#[table(order = <usize>)]`. Here, columns will
103-
//! be sorted based on their order. For e.g., column with `order = 0` will be displayed on the left followed by
104-
//! column with `order = 1` and so on.
105-
//! - `display_fn`: Used to print types which do not implement `Display` trait. Usage `#[table(display_fn = "<func_name>")]`.
106-
//! Signature of provided function should be `fn <func_name>(value: &<type>) -> impl Display`.
107-
//! - `customize_fn`: Used to customize style of a cell. Usage `#[table(customize_fn = "<func_name>")]`. Signature of
108-
//! provided function should be `fn <func_name>(cell: CellStruct, value: &<type>) -> CellStruct`. This attribute can
109-
//! be used when you want to change the formatting/style of a cell based on its contents. Note that this will
110-
//! overwrite all the style settings done by other attributes.
111-
//! - `skip`: Used to skip a field from table. Usage: `#[table(skip)]`
112-
//!
113-
//! For more information on configurations available on derive macro, go to `cli-table/examples/struct.rs`.
114-
//!
115-
//! ## CSV
116-
//!
117-
//! This crate also integrates with [`csv`](https://crates.io/crates/csv) crate. On enabling `"csv"` feature, you can
118-
//! use `TryFrom<&mut Reader> for TableStruct` trait implementation to convert `csv::Reader` to `TableStruct`.
119-
//!
120-
//! For more information on handling CSV values, go to `cli-table/examples/csv.rs`.
121-
//!
122-
//! # Styling
123-
//!
124-
//! Style of a table/cell can be modified by calling functions of [`Style`] trait. It is implementated by both
125-
//! [`TableStruct`] and [`CellStruct`].
126-
//!
127-
//! For individually formatting each cell of a table, `justify`, `align` and `padding` functions can be used from
128-
//! `CellStruct`.
129-
//!
130-
//! In addition to this, borders and separators of a table can be customized by calling `border` and `separator`
131-
//! functions in `TableStruct`. For example, to create a borderless table:
132-
//!
133-
//! ```rust,ignore
134-
//! use cli_table::{Cell, Table, TableStruct, format::{Justify, Border}, print_stdout};
135-
//!
136-
//! fn get_table() -> TableStruct {
137-
//! vec![
138-
//! vec!["Tom".cell(), 10.cell().justify(Justify::Right)],
139-
//! vec!["Jerry".cell(), 15.cell().justify(Justify::Right)],
140-
//! vec!["Scooby Doo".cell(), 20.cell().justify(Justify::Right)],
141-
//! ]
142-
//! .table()
143-
//! }
144-
//!
145-
//! let table = get_table().border(Border::builder().build()); // Attaches an empty border to the table
146-
//! assert!(print_stdout(table).is_ok());
147-
//! ```
148-
//!
149-
//! # Features
150-
//!
151-
//! - `derive`: Enables derive macro for creating tables using structs. **Enabled** by default.
152-
//! - `csv`: Enables support for printing tables using [`csv`](https://crates.io/crates/csv). **Enabled** by default.
7+
//! For more details, see [`cli-table`](https://docs.rs/cli-table).
1538
mod context;
1549
mod table;
15510
mod utils;

cli-table/README.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,45 @@ Below is the output of the table we created just now:
5050
+------------+----------------+
5151
```
5252

53+
### `Display` trait implementation
54+
55+
To get a `Display` trait implementation of `TableStruct`, use `display()` function on the struct to get an instance
56+
of `TableDisplay` which implements `Display` trait.
57+
58+
```rust
59+
use cli_table::{format::Justify, print_stdout, Cell, Style, Table};
60+
61+
let table = vec![
62+
vec!["Tom".cell(), 10.cell().justify(Justify::Right)],
63+
vec!["Jerry".cell(), 15.cell().justify(Justify::Right)],
64+
vec!["Scooby Doo".cell(), 20.cell().justify(Justify::Right)],
65+
]
66+
.table()
67+
.title(vec![
68+
"Name".cell().bold(true),
69+
"Age (in years)".cell().bold(true),
70+
])
71+
.bold(true);
72+
73+
let table_display = table.display().unwrap();
74+
75+
println!("{}", table_display);
76+
```
77+
78+
Below is the output of the table we created just now:
79+
80+
```markdown
81+
+------------+----------------+
82+
| Name | Age (in years) | <-- This row and all the borders/separators
83+
+------------+----------------+ will appear in bold
84+
| Tom | 10 |
85+
+------------+----------------+
86+
| Jerry | 15 |
87+
+------------+----------------+
88+
| Scooby Doo | 25 |
89+
+------------+----------------+
90+
```
91+
5392
### Derive macro
5493

5594
`#[derive(Table)]` can also be used to print a `Vec` or slice of `struct`s as table.

cli-table/src/buffers.rs

Lines changed: 115 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,120 @@
1-
use std::io::Result;
1+
use std::io::{Result, Write};
22

3-
use termcolor::BufferWriter;
3+
use termcolor::{Buffer, BufferWriter, ColorSpec, WriteColor};
44

5-
pub(crate) trait Buffers {
6-
type Dimension;
5+
pub struct Buffers<'a> {
6+
pub writer: &'a BufferWriter,
7+
buffers: Vec<Buffer>,
8+
current_buffer: Option<Buffer>,
9+
}
10+
11+
impl<'a> Buffers<'a> {
12+
pub fn new(writer: &'a BufferWriter) -> Self {
13+
Buffers {
14+
writer,
15+
buffers: Vec::new(),
16+
current_buffer: None,
17+
}
18+
}
19+
20+
pub fn push(&mut self, buffer: Buffer) -> Result<()> {
21+
if let Some(mut current_buffer) = self.current_buffer.take() {
22+
current_buffer.reset()?;
23+
self.buffers.push(current_buffer);
24+
}
25+
26+
self.buffers.push(buffer);
27+
28+
Ok(())
29+
}
30+
31+
pub fn append(&mut self, other: &mut Vec<Buffer>) -> Result<()> {
32+
if let Some(mut current_buffer) = self.current_buffer.take() {
33+
current_buffer.reset()?;
34+
self.buffers.push(current_buffer);
35+
}
36+
37+
self.buffers.append(other);
38+
39+
Ok(())
40+
}
41+
42+
pub fn end(&mut self) -> Result<()> {
43+
if let Some(mut current_buffer) = self.current_buffer.take() {
44+
current_buffer.reset()?;
45+
self.buffers.push(current_buffer);
46+
}
47+
48+
Ok(())
49+
}
50+
51+
pub fn into_vec(self) -> Result<Vec<Buffer>> {
52+
let mut buffers = self.buffers;
53+
54+
if let Some(mut buffer) = self.current_buffer {
55+
buffer.reset()?;
56+
buffers.push(buffer);
57+
}
58+
59+
Ok(buffers)
60+
}
61+
}
62+
63+
impl<'a> Write for Buffers<'a> {
64+
fn write(&mut self, buf: &[u8]) -> Result<usize> {
65+
if let Some(ref mut current_buffer) = self.current_buffer {
66+
current_buffer.write(buf)
67+
} else {
68+
let mut new_buffer = self.writer.buffer();
69+
let num_bytes = new_buffer.write(buf)?;
70+
self.current_buffer = Some(new_buffer);
71+
72+
Ok(num_bytes)
73+
}
74+
}
75+
76+
fn flush(&mut self) -> std::io::Result<()> {
77+
if let Some(ref mut current_buffer) = self.current_buffer {
78+
current_buffer.flush()
79+
} else {
80+
Ok(())
81+
}
82+
}
83+
}
84+
85+
impl<'a> WriteColor for Buffers<'a> {
86+
fn supports_color(&self) -> bool {
87+
match self.current_buffer {
88+
Some(ref buffer) => buffer.supports_color(),
89+
None => self.writer.buffer().supports_color(),
90+
}
91+
}
92+
93+
fn set_color(&mut self, color: &ColorSpec) -> Result<()> {
94+
if let Some(ref mut current_buffer) = self.current_buffer {
95+
current_buffer.set_color(color)
96+
} else {
97+
let mut new_buffer = self.writer.buffer();
98+
new_buffer.set_color(color)?;
99+
self.current_buffer = Some(new_buffer);
100+
101+
Ok(())
102+
}
103+
}
7104

8-
type Buffers;
105+
fn reset(&mut self) -> Result<()> {
106+
if let Some(ref mut current_buffer) = self.current_buffer {
107+
current_buffer.reset()
108+
} else {
109+
Ok(())
110+
}
111+
}
9112

10-
fn buffers(
11-
&self,
12-
writer: &BufferWriter,
13-
available_dimension: Self::Dimension,
14-
) -> Result<Self::Buffers>;
113+
fn is_synchronous(&self) -> bool {
114+
if let Some(ref buffer) = self.current_buffer {
115+
buffer.is_synchronous()
116+
} else {
117+
self.writer.buffer().is_synchronous()
118+
}
119+
}
15120
}

0 commit comments

Comments
 (0)