styling tables compatible to windows

This commit is contained in:
Nikolas Schmidt-Voigt 2021-07-30 17:54:10 +02:00
parent 612b82cefa
commit ad97302723
5 changed files with 90 additions and 106 deletions

49
Cargo.lock generated Normal file → Executable file
View file

@ -11,6 +11,15 @@ dependencies = [
"winapi",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "anyhow"
version = "1.0.42"
@ -38,10 +47,10 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
name = "bartib"
version = "0.1.0"
dependencies = [
"ansi_term 0.12.1",
"anyhow",
"chrono",
"clap",
"termion",
"thiserror",
]
@ -70,7 +79,7 @@ version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"ansi_term 0.11.0",
"atty",
"bitflags",
"strsim",
@ -113,12 +122,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "numtoa"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef"
[[package]]
name = "proc-macro2"
version = "1.0.24"
@ -137,24 +140,6 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "redox_syscall"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ab49abadf3f9e1c4bc499e8845e152ad87d2ad2d30371841171169e9d75feee"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_termios"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
dependencies = [
"redox_syscall",
]
[[package]]
name = "strsim"
version = "0.8.0"
@ -172,18 +157,6 @@ dependencies = [
"unicode-xid",
]
[[package]]
name = "termion"
version = "1.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
dependencies = [
"libc",
"numtoa",
"redox_syscall",
"redox_termios",
]
[[package]]
name = "textwrap"
version = "0.11.0"

2
Cargo.toml Normal file → Executable file
View file

@ -11,4 +11,4 @@ chrono = "0.4"
clap = "~2.33"
thiserror = "1.0"
anyhow = "1.0.42"
termion = "*"
ansi_term = "0.12.1"

View file

@ -7,7 +7,7 @@ Bartib is a time tracker for the command line. It safes a journal of all tracked
Build it with cargo:
```
cargo build --release
cargo build --release --bin bartib
```
## The journal file

16
src/output.rs Normal file → Executable file
View file

@ -1,4 +1,4 @@
use termion::color;
use ansi_term::Colour;
use chrono::NaiveDate;
use std::collections::BTreeMap;
@ -22,12 +22,10 @@ pub fn list_activities(activities: &[&activity::Activity], with_start_dates: boo
"Duration".to_string(),
]);
let rows: Vec<table::Row> = activities
activities
.iter()
.map(|t| get_activity_table_row(&t, with_start_dates))
.collect();
rows.iter().for_each(|row| activity_table.add_row(&row));
.for_each(|row| activity_table.add_row(row));
println!("\n{}", activity_table);
}
@ -61,7 +59,7 @@ pub fn list_running_activities(running_activities: &[&activity::Activity]) {
"Duration".to_string()
]);
let rows: Vec<table::Row> = running_activities
running_activities
.iter()
.map(|activity | {
table::Row::new(vec![
@ -71,9 +69,7 @@ pub fn list_running_activities(running_activities: &[&activity::Activity]) {
format_util::format_duration(&activity.get_duration()),
])
})
.collect();
rows.iter().for_each(|row| activity_table.add_row(&row));
.for_each(|row| activity_table.add_row(row));
println!("\n{}", activity_table);
}
@ -122,7 +118,7 @@ fn get_activity_table_row(activity: &&activity::Activity, with_start_dates: bool
]);
if !activity.is_stopped() {
new_row.with_format(color::Fg(color::Green).to_string(), color::Fg(color::Reset).to_string());
new_row.set_color(Colour::Green);
}
new_row

127
src/table.rs Normal file → Executable file
View file

@ -1,56 +1,55 @@
use std::cmp;
use std::fmt;
use std::fmt::Formatter;
use std::str;
use termion::style;
use ansi_term::{Colour, Style};
pub struct Row {
content: Vec<String>,
before: String,
after: String,
color: Option<Colour>,
}
pub struct Group<'a> {
pub struct Group {
title: Option<String>,
rows: Vec<&'a Row>
rows: Vec<Row>,
}
pub struct Table<'a> {
pub struct Table {
header: Vec<String>,
rows: Vec<&'a Row>,
groups: Vec<&'a Group<'a>>,
rows: Vec<Row>,
groups: Vec<Group>,
}
impl Row{
impl Row {
pub fn new(content: Vec<String>) -> Row {
Row {
content: content,
before: "".to_string(),
after: "".to_string()
Row {
content,
color: None,
}
}
pub fn with_format(&mut self, before: String, after: String) {
self.before = before;
self.after = after;
pub fn set_color(&mut self, color: Colour) {
self.color = Some(color);
}
}
impl<'a> Group<'a> {
pub fn new(title: Option<String>, rows: Vec<&'a Row>) -> Group<'a> {
Group {title, rows}
impl Group {
pub fn new(title: Option<String>, rows: Vec<Row>) -> Group {
Group { title, rows }
}
}
impl<'a> Table<'a> {
pub fn new(header: Vec<String>) -> Table<'a> {
impl Table {
pub fn new(header: Vec<String>) -> Table {
Table {
header,
groups: Vec::new(),
rows: Vec::new()
rows: Vec::new(),
}
}
pub fn add_row(&mut self, row: &'a Row) {
pub fn add_row(&mut self, row: Row) {
self.rows.push(row);
}
@ -75,45 +74,61 @@ impl<'a> Table<'a> {
column_width
}
}
fn write_cells<T: AsRef<str> + std::fmt::Display>(
column_width: &[usize],
cells: &[T],
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
let mut i = 0;
impl fmt::Display for Table {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let column_width = self.get_column_width();
while let Some(cell) = cells.get(i) {
if let Some(width) = column_width.get(i) {
write!(f, "{:<width$} ", cell, width = width)?;
} else {
write!(f, "{} ", cell)?;
}
write_cells(f, &self.header, &column_width, Some(Style::new().underline()))?;
writeln!(f)?;
i += 1;
for row in &self.rows {
let style = row.color.map(|color| Style::new().fg(color));
write_cells(f, &row.content, &column_width, style)?;
writeln!(f)?;
}
Ok(())
}
}
impl<'a> fmt::Display for Table<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let column_width = self.get_column_width();
fn write_cells<T: AsRef<str> + std::fmt::Display>(
f: &mut fmt::Formatter<'_>,
cells: &[T],
column_width: &[usize],
style: Option<Style>,
) -> fmt::Result {
let cells_with_width : Vec<(Option<&usize>, &str)> = cells.iter()
.map(|cell| cell.as_ref())
.enumerate()
.map(|(i, cell)| (column_width.get(i), cell))
.collect();
write!(f, "{}", style::Underline)?;
Table::write_cells(&column_width, &self.header, f)?;
writeln!(f, "{}", style::NoUnderline)?;
for row in &self.rows {
write!(f, "{}", &row.before)?;
Table::write_cells(&column_width, &row.content, f)?;
write!(f, "{}", &row.after)?;
writeln!(f)?;
}
Ok(())
for (width, cell) in cells_with_width {
write_with_width_and_style(f, cell, width, style)?;
}
Ok(())
}
fn write_with_width_and_style(
f: &mut fmt::Formatter<'_>,
content: &str,
opt_width: Option<&usize>,
opt_style: Option<Style>,
) -> fmt::Result {
let content_length = content.chars().count();
let style_prefix = opt_style.map_or("".to_string(), |style| style.prefix().to_string());
let style_suffix = opt_style.map_or("".to_string(), |style| style.suffix().to_string());
let width = opt_width.unwrap_or(&content_length);
write!(f, "{prefix}{content:<width$}{suffix} ",
prefix = style_prefix,
content = content,
width = width,
suffix = style_suffix
)
}
#[cfg(test)]
@ -126,8 +141,8 @@ mod tests {
let row1 = Row::new(vec!["abc".to_string(), "defg".to_string()]);
let row2 = Row::new(vec!["a".to_string(), "b".to_string(), "cdef".to_string()]);
t.add_row(&row1);
t.add_row(&row2);
t.add_row(row1);
t.add_row(row2);
let column_width = t.get_column_width();
@ -142,12 +157,12 @@ mod tests {
let row1 = Row::new(vec!["abc".to_string(), "defg".to_string()]);
let row2 = Row::new(vec!["a".to_string(), "b".to_string(), "cdef".to_string()]);
t.add_row(&row1);
t.add_row(&row2);
t.add_row(row1);
t.add_row(row2);
assert_eq!(
format!("{}", t),
"a b c \n--- ---- ---- \nabc defg \na b cdef \n"
"\u{1b}[4ma \u{1b}[0m \u{1b}[4mb \u{1b}[0m \u{1b}[4mc \u{1b}[0m \nabc defg \na b cdef \n"
);
}
}