mirror of
https://github.com/nikolassv/bartib
synced 2024-11-28 14:30:25 +00:00
styling tables compatible to windows
This commit is contained in:
parent
612b82cefa
commit
ad97302723
5 changed files with 90 additions and 106 deletions
49
Cargo.lock
generated
Normal file → Executable file
49
Cargo.lock
generated
Normal file → Executable 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
2
Cargo.toml
Normal file → Executable file
|
@ -11,4 +11,4 @@ chrono = "0.4"
|
|||
clap = "~2.33"
|
||||
thiserror = "1.0"
|
||||
anyhow = "1.0.42"
|
||||
termion = "*"
|
||||
ansi_term = "0.12.1"
|
|
@ -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
16
src/output.rs
Normal file → Executable 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
127
src/table.rs
Normal file → Executable 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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue