Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented horizontal span #85

Merged
merged 1 commit into from
Sep 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,5 @@
language: rust
rust:
- 1.20.0
- 1.21.0
- 1.22.1
- 1.23.0
- 1.24.1
- 1.25.0
- 1.26.2
- 1.27.2
- 1.28.0
Expand Down
27 changes: 15 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Include the library as a dependency to your project by adding the following line
prettytable-rs = "^0.7"
```

The library requires at least `rust v1.20.0`.
The library requires at least `rust v1.26.0`.

## Basic usage

Expand Down Expand Up @@ -143,7 +143,7 @@ Rows may have different numbers of cells. The table will automatically adapt to

## Do it with style!

Tables can have a styled output with background and foreground colors, bold and italic as configurable settings, thanks to the `term` crate.
Tables can have a styled output with background and foreground colors, bold and italic as configurable settings, thanks to the `term` crate. Alignment in cells can also be set (Left, Right, Center), and a cell can span accross multiple columns.

`term` style attributes are reexported

Expand All @@ -155,35 +155,37 @@ Tables can have a styled output with background and foreground colors, bold and

table.add_row(Row::new(vec![
Cell::new("foobar")
.with_style(Attr::Bold),
.with_style(Attr::ForegroundColor(color::GREEN))
.with_style(Attr::Bold)
.with_style(Attr::ForegroundColor(color::GREEN)),
Cell::new("bar")
.with_style(Attr::BackgroundColor(color::RED)),
.with_style(Attr::Italic(true)),
Cell::new("foo")]));
.with_style(Attr::BackgroundColor(color::RED))
.with_style(Attr::Italic(true))
.with_hspan(2),
Cell::new("foo")
]));
```

- through style strings:
```rust
table.add_row(Row::new(vec![
Cell::new("foobar").style_spec("bFg"),
Cell::new("bar").style_spec("Bri"),
Cell::new("bar").style_spec("BriH2"),
Cell::new("foo")]));
```

- using `row!` macro:
```rust
table.add_row(row![bFg->"foobar", Bri->"bar", "foo"]);
table.add_row(row![bFg->"foobar", BriH2->"bar", "foo"]);
```

- using `table!` macro (this one creates a new table, unlike previous examples):
```rust
table!([bFg->"foobar", Bri->"bar", "foo"]);
table!([bFg->"foobar", BriH2->"bar", "foo"]);
```

Here
- **bFg** means **bold**, **F**oreground: **g**reen,
- **Bri** means **B**ackground: **r**ed, **i**talic.
- **BriH2** means **B**ackground: **r**ed, **i**talic, **H**orizontal span of **2**.

Another example: **FrBybc** means **F**oreground: **r**ed, **B**ackground: **y**ellow, **b**old, **c**enter.

Expand Down Expand Up @@ -214,6 +216,7 @@ All cases of styling cells in macros:

* **F** : **F**oreground (must be followed by a color specifier)
* **B** : **B**ackground (must be followed by a color specifier)
* **H** : **H**orizontal span (must be followed by a number)
* **b** : **b**old
* **i** : **i**talic
* **u** : **u**nderline
Expand Down Expand Up @@ -375,6 +378,6 @@ Since `v0.6.3`, platform specific line endings are activated though the default
When this feature is deactivated (for instance with the `--no-default-features` flag in cargo), line endings will be rendered with `\n`
on any platform.

This customization capability will probably move to Formatting API in `v0.7`.
This customization capability will probably move to Formatting API in a future release.

Additional examples are provided in the documentation and in [examples](./examples/) directory.
31 changes: 31 additions & 0 deletions examples/span.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#[macro_use]
extern crate prettytable;
use prettytable::{row::Row, cell::Cell, format::Alignment};


fn main() {

/*
The following code will output

+---------------+---------------+--------------+
| A table with horizontal span |
+===============+===============+==============+
| This is a cell with span of 2 | span of 1 |
+---------------+---------------+--------------+
| span of 1 | span of 1 | span of 1 |
+---------------+---------------+--------------+
| This cell with a span of 3 is centered |
+---------------+---------------+--------------+
*/

let mut table: prettytable::Table = table![
[H2 -> "This is a cell with span of 2", "span of 1"],
["span of 1", "span of 1", "span of 1"],
[H03c -> "This cell with a span of 3 is centered"]
];
table.set_titles(Row::new(vec![
Cell::new_align("A table with horizontal span", Alignment::CENTER).with_hspan(3)
]));
table.printstd();
}
106 changes: 75 additions & 31 deletions src/cell.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
//! This module contains definition of table/row cells stuff

use std::io::{Write, Error};
use std::string::ToString;
use super::{Attr, Terminal, color};
use super::format::Alignment;
use super::utils::display_width;
use super::utils::print_align;
use super::{color, Attr, Terminal};
use std::io::{Error, Write};
use std::string::ToString;
use std::str::FromStr;

/// Represent a table cell containing a string.
///
Expand All @@ -17,6 +18,7 @@ pub struct Cell {
width: usize,
align: Alignment,
style: Vec<Attr>,
hspan: usize,
}

impl Cell {
Expand All @@ -36,6 +38,7 @@ impl Cell {
width: width,
align: align,
style: Vec::new(),
hspan: 1,
}
}

Expand All @@ -61,6 +64,12 @@ impl Cell {
self
}

/// Add horizontal spanning to the cell
pub fn with_hspan(mut self, hspan: usize) -> Cell {
self.set_hspan(hspan);
self
}

/// Remove all style attributes and reset alignment to default (LEFT)
pub fn reset_style(&mut self) {
self.style.clear();
Expand Down Expand Up @@ -107,7 +116,8 @@ impl Cell {
self.reset_style();
let mut foreground = false;
let mut background = false;
for c in spec.chars() {
let mut it = spec.chars().peekable();
while let Some(c) = it.next() {
if foreground || background {
let color = match c {
'r' => color::RED,
Expand Down Expand Up @@ -150,6 +160,14 @@ impl Cell {
'c' => self.align(Alignment::CENTER),
'l' => self.align(Alignment::LEFT),
'r' => self.align(Alignment::RIGHT),
'H' => {
let mut span_s = String::new();
while let Some('0'..='9') = it.peek() {
span_s.push(it.next().unwrap());
}
let span = usize::from_str(&span_s).unwrap();
self.set_hspan(span);
}
_ => { /* Silently ignore unknown tags */ }
}
}
Expand All @@ -167,6 +185,16 @@ impl Cell {
self.width
}

/// Set horizontal span for this cell (must be > 0)
pub fn set_hspan(&mut self, hspan: usize) {
self.hspan = if hspan <= 0 {1} else {hspan};
}

/// Get horizontal span of this cell (> 0)
pub fn get_hspan(&self) -> usize {
self.hspan
}

/// Return a copy of the full string contained in the cell
pub fn get_content(&self) -> String {
self.content.join("\n")
Expand All @@ -176,36 +204,38 @@ impl Cell {
/// `idx` is the line index to print. `col_width` is the column width used to
/// fill the cells with blanks so it fits in the table.
/// If `ìdx` is higher than this cell's height, it will print empty content
pub fn print<T: Write + ?Sized>(&self,
out: &mut T,
idx: usize,
col_width: usize,
skip_right_fill: bool)
-> Result<(), Error> {
pub fn print<T: Write + ?Sized>(
&self,
out: &mut T,
idx: usize,
col_width: usize,
skip_right_fill: bool,
) -> Result<(), Error> {
let c = self.content.get(idx).map(|s| s.as_ref()).unwrap_or("");
print_align(out, self.align, c, ' ', col_width, skip_right_fill)
}

/// Apply style then call `print` to print the cell into a terminal
pub fn print_term<T: Terminal + ?Sized>(&self,
out: &mut T,
idx: usize,
col_width: usize,
skip_right_fill: bool)
-> Result<(), Error> {
pub fn print_term<T: Terminal + ?Sized>(
&self,
out: &mut T,
idx: usize,
col_width: usize,
skip_right_fill: bool,
) -> Result<(), Error> {
for a in &self.style {
match out.attr(*a) {
Ok(..) |
Err(::term::Error::NotSupported) |
Err(::term::Error::ColorOutOfRange) => (), // Ignore unsupported atrributes
Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => {
()
} // Ignore unsupported atrributes
Err(e) => return Err(term_error_to_io_error(e)),
};
}
self.print(out, idx, col_width, skip_right_fill)?;
match out.reset() {
Ok(..) |
Err(::term::Error::NotSupported) |
Err(::term::Error::ColorOutOfRange) => Ok(()),
Ok(..) | Err(::term::Error::NotSupported) | Err(::term::Error::ColorOutOfRange) => {
Ok(())
}
Err(e) => Err(term_error_to_io_error(e)),
}
}
Expand Down Expand Up @@ -238,6 +268,7 @@ impl Default for Cell {
width: 0,
align: Alignment::LEFT,
style: Vec::new(),
hspan: 1,
}
}
}
Expand Down Expand Up @@ -271,17 +302,23 @@ impl Default for Cell {
/// ```
#[macro_export]
macro_rules! cell {
() => ($crate::cell::Cell::default());
($value:expr) => ($crate::cell::Cell::new(&$value.to_string()));
($style:ident -> $value:expr) => (cell!($value).style_spec(stringify!($style)));
() => {
$crate::cell::Cell::default()
};
($value:expr) => {
$crate::cell::Cell::new(&$value.to_string())
};
($style:ident -> $value:expr) => {
cell!($value).style_spec(stringify!($style))
};
}

#[cfg(test)]
mod tests {
use cell::Cell;
use utils::StringWriter;
use format::Alignment;
use term::{Attr, color};
use term::{color, Attr};
use utils::StringWriter;

#[test]
fn get_content() {
Expand Down Expand Up @@ -350,14 +387,18 @@ mod tests {
assert!(cell.style.contains(&Attr::Italic(true)));
assert!(cell.style.contains(&Attr::Bold));
assert!(cell.style.contains(&Attr::ForegroundColor(color::RED)));
assert!(cell.style
.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE)));
assert!(
cell.style
.contains(&Attr::BackgroundColor(color::BRIGHT_BLUE))
);
assert_eq!(cell.align, Alignment::CENTER);

cell = cell.style_spec("FDBwr");
assert_eq!(cell.style.len(), 2);
assert!(cell.style
.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK)));
assert!(
cell.style
.contains(&Attr::ForegroundColor(color::BRIGHT_BLACK))
);
assert!(cell.style.contains(&Attr::BackgroundColor(color::WHITE)));
assert_eq!(cell.align, Alignment::RIGHT);

Expand All @@ -368,6 +409,9 @@ mod tests {
assert_eq!(cell.style.len(), 1);
cell = cell.style_spec("zzz");
assert!(cell.style.is_empty());
assert_eq!(cell.get_hspan(), 1);
cell = cell.style_spec("FDBwH03r");
assert_eq!(cell.get_hspan(), 3);
}

#[test]
Expand Down
28 changes: 25 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl<'a> TableSlice<'a> {
pub fn get_column_num(&self) -> usize {
let mut cnum = 0;
for r in self.rows {
let l = r.len();
let l = r.column_count();
if l > cnum {
cnum = l;
}
Expand All @@ -102,11 +102,11 @@ impl<'a> TableSlice<'a> {
/// Return 0 if the column does not exists;
fn get_column_width(&self, col_idx: usize) -> usize {
let mut width = match *self.titles {
Some(ref t) => t.get_cell_width(col_idx),
Some(ref t) => t.get_column_width(col_idx, self.format),
None => 0,
};
for r in self.rows {
let l = r.get_cell_width(col_idx);
let l = r.get_column_width(col_idx, self.format);
if l > width {
width = l;
}
Expand All @@ -120,6 +120,7 @@ impl<'a> TableSlice<'a> {
let colnum = self.get_column_num();
let mut col_width = vec![0usize; colnum];
for i in 0..colnum {
// TODO: calling "get_column_width()" in a loop is inefficient
col_width[i] = self.get_column_width(i);
}
col_width
Expand Down Expand Up @@ -1030,6 +1031,27 @@ mod tests {
assert_eq!(out, table.to_string().replace("\r\n","\n"));
}

#[test]
fn test_horizontal_span() {
let mut table = Table::new();
table.set_titles(Row::new(vec![Cell::new("t1"), Cell::new("t2").with_hspan(2)]));
table.add_row(Row::new(vec![Cell::new("a"), Cell::new("bc"), Cell::new("def")]));
table.add_row(Row::new(vec![Cell::new("def").style_spec("H02c"), Cell::new("a")]));
let out = "\
+----+----+-----+
| t1 | t2 |
+====+====+=====+
| a | bc | def |
+----+----+-----+
| def | a |
+----+----+-----+
";
println!("{}", out);
println!("____");
println!("{}", table.to_string().replace("\r\n","\n"));
assert_eq!(out, table.to_string().replace("\r\n","\n"));
}

#[cfg(feature = "csv")]
mod csv {
use Table;
Expand Down
Loading