a more comfortable API for tabulate

This commit is contained in:
Abraham Toriz 2022-07-21 21:35:45 +08:00
parent b53807c4a6
commit 8b64b161f2
No known key found for this signature in database
GPG Key ID: D5B4A746DB5DD42A
4 changed files with 106 additions and 108 deletions

View File

@ -86,11 +86,11 @@ impl<'a> Command<'a> for ListCommand {
(
if current == key {
"*".into()
"*"
} else if last.as_ref() == Some(&key) {
"-".into()
"-"
} else {
"".into()
""
},
key,
@ -106,37 +106,37 @@ impl<'a> Command<'a> for ListCommand {
let mut tabs = Tabulate::with_columns(vec![
// indicator of current or prev sheet
Col::min_width(1).and_alignment(Right),
Col::new().min_width(1).and_alignment(Right),
// sheet name
Col::min_width(9).and_alignment(Left),
Col::new().min_width(9).and_alignment(Left),
// running time
Col::min_width(9).and_alignment(Right)
Col::new().min_width(9).and_alignment(Right)
.color_if(Style::new().dimmed(), |s| s == "0:00:00")
.color_if(Style::new().bold(), |s| s != "0:00:00"),
// today
Col::min_width(9).and_alignment(Right)
Col::new().min_width(9).and_alignment(Right)
.color_if(Style::new().dimmed(), |s| s == "0:00:00")
.color_if(Style::new().bold(), |s| s != "0:00:00"),
// accumulated
Col::min_width(12).and_alignment(Right),
Col::new().min_width(12).and_alignment(Right),
]);
tabs.feed(vec!["".into(), "Timesheet".into(), "Running".into(), "Today".into(), "Total Time".into()]);
tabs.feed(vec!["", "Timesheet", "Running", "Today", "Total Time"]);
tabs.separator(' ');
for sheet in sheets {
tabs.feed(vec![sheet.0, sheet.1, sheet.2, sheet.3, sheet.4]);
tabs.feed(vec![sheet.0.to_string(), sheet.1, sheet.2, sheet.3, sheet.4]);
}
tabs.separator('-');
tabs.feed(vec![
"".into(),
"".into(),
"".to_string(),
"".to_string(),
format_duration(total_running),
format_duration(total_today),
format_duration(total),

View File

@ -45,33 +45,33 @@ impl<'a> Command<'a> for NowCommand {
let mut tabs = Tabulate::with_columns(vec![
// indicator of current or prev sheet
Col::min_width(1).and_alignment(Right),
Col::new().min_width(1).and_alignment(Right),
// sheet name
Col::min_width(9).and_alignment(Left),
Col::new().min_width(9).and_alignment(Left),
// running time
Col::min_width(9).and_alignment(Right),
Col::new().min_width(9).and_alignment(Right),
// activity
Col::min_width(0).and_alignment(Left),
Col::new().min_width(0).and_alignment(Left),
]);
tabs.feed(vec!["".into(), "Timesheet".into(), "Running".into(), "Activity".into()]);
tabs.feed(vec!["", "Timesheet", "Running", "Activity"]);
tabs.separator(' ');
for entry in entries {
tabs.feed(vec![
if current == entry.sheet {
"*".into()
"*"
} else if last.as_ref() == Some(&entry.sheet) {
"-".into()
"-"
} else {
"".into()
},
""
}.to_string(),
entry.sheet,
format_duration(facts.now - entry.start),
entry.note.unwrap_or_else(|| "".into())
entry.note.unwrap_or_else(|| "".to_string())
]);
}
@ -107,7 +107,7 @@ mod tests {
streams.db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), Some(Utc.ymd(2021, 1, 1).and_hms(10,13, 55)), None, "sheet1").unwrap();
streams.db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(0, 0, 0), Some(Utc.ymd(2021, 1, 1).and_hms(7, 39, 18)), None, "sheet3").unwrap();
streams.db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(12, 0, 0), Some(Utc.ymd(2021, 1, 1).and_hms(13, 52, 45)), None, "sheet3").unwrap();
streams.db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(12, 0, 0), None, Some("some".into()), "sheet4").unwrap();
streams.db.entry_insert(Utc.ymd(2021, 1, 1).and_hms(12, 0, 0), None, Some("some".to_string()), "sheet4").unwrap();
NowCommand::handle(Default::default(), &mut streams, &facts).unwrap();

View File

@ -97,7 +97,7 @@ pub fn print_formatted<W: Write>(entries: Vec<Entry>, out: &mut W, facts: &Facts
total = total + daily;
tabs.feed(vec!["".into(), "".into(), "".into(), format_duration(daily), "".into()]);
tabs.feed(vec!["".to_string(), "".to_string(), "".to_string(), format_duration(daily), "".to_string()]);
}
tabs.separator('-');
@ -109,16 +109,16 @@ pub fn print_formatted<W: Write>(entries: Vec<Entry>, out: &mut W, facts: &Facts
if num_sheets > 1 {
let mut tabs = Tabulate::with_columns(vec![
Col::min_width(3).and_alignment(Right),
Col::min_width(18).and_alignment(Left),
Col::min_width(21).and_alignment(Left),
Col::min_width(8).and_alignment(Right),
Col::min_width(5).max_width(44).and_alignment(Left),
Col::new().min_width(3).and_alignment(Right),
Col::new().min_width(18).and_alignment(Left),
Col::new().min_width(21).and_alignment(Left),
Col::new().min_width(8).and_alignment(Right),
Col::new().min_width(5).max_width(44).and_alignment(Left),
]);
tabs.separator('-');
tabs.feed(vec!["".into(), "Grand total".into(), "".into(), format_duration(grand_total)]);
tabs.feed(vec!["".to_string(), "Grand total".to_string(), "".to_string(), format_duration(grand_total)]);
out.write_all(tabs.print(facts.env.stdout_is_tty).as_bytes())?;
}
@ -340,17 +340,17 @@ mod tests {
let entries = vec![
Entry {
id: 1,
sheet: "sheet1".into(),
sheet: "sheet1".to_string(),
start: Utc.ymd(2008, 10, 5).and_hms(16, 0, 0),
end: Some(Utc.ymd(2008, 10, 5).and_hms(18, 0, 0)),
note: Some("quiúbole".into()),
note: Some("quiúbole".to_string()),
},
Entry {
id: 2,
sheet: "sheet2".into(),
sheet: "sheet2".to_string(),
start: Utc.ymd(2008, 10, 5).and_hms(16, 0, 0),
end: Some(Utc.ymd(2008, 10, 5).and_hms(18, 0, 0)),
note: Some("quiúbole".into()),
note: Some("quiúbole".to_string()),
},
];

View File

@ -45,12 +45,10 @@ impl Col {
}
}
pub fn min_width(size: usize) -> Col {
pub fn min_width(self, size: usize) -> Col {
Col {
min_width: size,
align: Align::Left,
max_width: None,
conditonal_styles: Vec::new(),
..self
}
}
@ -106,11 +104,11 @@ impl Tabulate {
}
}
pub fn feed(&mut self, data: Vec<String>) {
pub fn feed<T: AsRef<str>>(&mut self, data: Vec<T>) {
let mut lines: Vec<Vec<String>> = Vec::new();
for (col, ((w, d), c)) in self.widths.iter_mut().zip(data.iter()).zip(self.cols.iter()).enumerate() {
for (r1, dl) in d.split('\n').enumerate() {
for (r1, dl) in d.as_ref().split('\n').enumerate() {
for (r2, l) in constrained_lines(dl, c.max_width.unwrap_or(usize::MAX)).into_iter().enumerate() {
let count = l.chars().count();
@ -126,8 +124,8 @@ impl Tabulate {
}
} else {
lines.push({
let mut prev = if (r1 + r2) == 0 {
data[..col].to_vec()
let mut prev: Vec<_> = if (r1 + r2) == 0 {
data[..col].iter().map(|s| s.as_ref().to_string()).collect()
} else {
(0..col).map(|_| "".into()).collect()
};
@ -220,21 +218,21 @@ mod tests {
#[test]
fn test_text_output() {
let mut tabs = Tabulate::with_columns(vec![
Col::min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::min_width("Duration".len()).and_alignment(Right),
Col::min_width("Notes".len()).and_alignment(Left),
Col::new().min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::new().min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::new().min_width("Duration".len()).and_alignment(Right),
Col::new().min_width("Notes".len()).and_alignment(Left),
]);
tabs.feed(vec!["Day".into(), "Start End".into(), "Duration".into(), "Notes".into()]);
tabs.feed(vec!["Fri Oct 03, 2008".into(), "12:00:00 - 14:00:00".into(), "2:00:00".into(), "entry 1".into()]);
tabs.feed(vec!["".into(), "16:00:00 - 18:00:00".into(), "2:00:00".into(), "entry 2".into()]);
tabs.feed(vec!["".into(), "".into(), "4:00:00".into(), "".into()]);
tabs.feed(vec!["Sun Oct 05, 2008".into(), "16:00:00 - 18:00:00".into(), "2:00:00".into(), "entry 3".into()]);
tabs.feed(vec!["".into(), "18:00:00 - ".into(), "2:00:00".into(), "entry 4".into()]);
tabs.feed(vec!["".into(), "".into(), "4:00:00".into(), "".into()]);
tabs.feed(vec!["Day", "Start End", "Duration", "Notes"]);
tabs.feed(vec!["Fri Oct 03, 2008", "12:00:00 - 14:00:00", "2:00:00", "entry 1"]);
tabs.feed(vec!["", "16:00:00 - 18:00:00", "2:00:00", "entry 2"]);
tabs.feed(vec!["", "", "4:00:00", ""]);
tabs.feed(vec!["Sun Oct 05, 2008", "16:00:00 - 18:00:00", "2:00:00", "entry 3"]);
tabs.feed(vec!["", "18:00:00 - ", "2:00:00", "entry 4"]);
tabs.feed(vec!["", "", "4:00:00", ""]);
tabs.separator('-');
tabs.feed(vec!["Total".into(), "".into(), "8:00:00".into(), "".into()]);
tabs.feed(vec!["Total", "", "8:00:00", ""]);
assert_eq!(&tabs.print(false), "\
Day Start End Duration Notes
@ -252,19 +250,19 @@ Total 8:00:00
#[test]
fn test_text_output_long_duration() {
let mut tabs = Tabulate::with_columns(vec![
Col::min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::min_width("12:00:00 - 14:00:00".len()).and_alignment(Left),
Col::min_width("Duration".len()).and_alignment(Right),
Col::min_width("Notes".len()).and_alignment(Left),
Col::new().min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::new().min_width("12:00:00 - 14:00:00".len()).and_alignment(Left),
Col::new().min_width("Duration".len()).and_alignment(Right),
Col::new().min_width("Notes".len()).and_alignment(Left),
]);
tabs.feed(vec!["Day".into(), "Start End".into(), "Duration".into(), "Notes".into()]);
tabs.feed(vec!["Wed Oct 01, 2008".into(), "12:00:00 - 14:00:00+2d".into(), "50:00:00".into(), "entry 1".into()]);
tabs.feed(vec!["".into(), "".into(), "50:00:00".into(), "".into()]);
tabs.feed(vec!["Fri Oct 03, 2008".into(), "12:00:00 - 14:00:00".into(), "2:00:00".into(), "entry 2".into()]);
tabs.feed(vec!["".into(), "".into(), "2:00:00".into(), "".into()]);
tabs.feed(vec!["Day", "Start End", "Duration", "Notes"]);
tabs.feed(vec!["Wed Oct 01, 2008", "12:00:00 - 14:00:00+2d", "50:00:00", "entry 1"]);
tabs.feed(vec!["", "", "50:00:00", ""]);
tabs.feed(vec!["Fri Oct 03, 2008", "12:00:00 - 14:00:00", "2:00:00", "entry 2"]);
tabs.feed(vec!["", "", "2:00:00", ""]);
tabs.separator('-');
tabs.feed(vec!["Total".into(), "".into(), "52:00:00".into(), "".into()]);
tabs.feed(vec!["Total", "", "52:00:00", ""]);
assert_eq!(&tabs.print(false), "\
Day Start End Duration Notes
@ -280,22 +278,22 @@ Total 52:00:00
#[test]
fn test_text_output_with_ids() {
let mut tabs = Tabulate::with_columns(vec![
Col::min_width(3).and_alignment(Right),
Col::min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::min_width("Duration".len()).and_alignment(Right),
Col::min_width("Notes".len()).and_alignment(Left),
Col::new().min_width(3).and_alignment(Right),
Col::new().min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::new().min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::new().min_width("Duration".len()).and_alignment(Right),
Col::new().min_width("Notes".len()).and_alignment(Left),
]);
tabs.feed(vec!["ID".into(), "Day".into(), "Start End".into(), "Duration".into(), "Notes".into()]);
tabs.feed(vec!["1".into(), "Fri Oct 03, 2008".into(), "12:00:00 - 14:00:00".into(), "2:00:00".into(), "entry 1".into()]);
tabs.feed(vec!["2".into(), "".into(), "16:00:00 - 18:00:00".into(), "2:00:00".into(), "entry 2".into()]);
tabs.feed(vec!["".into(), "".into(), "".into(), "4:00:00".into(), "".into()]);
tabs.feed(vec!["3".into(), "Sun Oct 05, 2008".into(), "16:00:00 - 18:00:00".into(), "2:00:00".into(), "entry 3".into()]);
tabs.feed(vec!["4".into(), "".into(), "18:00:00 -".into(), "2:00:00".into(), "entry 4".into()]);
tabs.feed(vec!["".into(), "".into(), "".into(), "4:00:00".into(), "".into()]);
tabs.feed(vec!["ID", "Day", "Start End", "Duration", "Notes"]);
tabs.feed(vec!["1", "Fri Oct 03, 2008", "12:00:00 - 14:00:00", "2:00:00", "entry 1"]);
tabs.feed(vec!["2", "", "16:00:00 - 18:00:00", "2:00:00", "entry 2"]);
tabs.feed(vec!["", "", "", "4:00:00", ""]);
tabs.feed(vec!["3", "Sun Oct 05, 2008", "16:00:00 - 18:00:00", "2:00:00", "entry 3"]);
tabs.feed(vec!["4", "", "18:00:00 -", "2:00:00", "entry 4"]);
tabs.feed(vec!["", "", "", "4:00:00", ""]);
tabs.separator('-');
tabs.feed(vec!["".into(), "Total".into(), "".into(), "8:00:00".into()]);
tabs.feed(vec!["", "Total", "", "8:00:00"]);
assert_eq!(&tabs.print(false), " ID Day Start End Duration Notes
1 Fri Oct 03, 2008 12:00:00 - 14:00:00 2:00:00 entry 1
@ -312,18 +310,18 @@ Total 52:00:00
#[test]
fn test_text_output_long_note_with_ids() {
let mut tabs = Tabulate::with_columns(vec![
Col::min_width(2).and_alignment(Right),
Col::min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::min_width("Duration".len()).and_alignment(Right),
Col::min_width("Notes".len()).max_width(44).and_alignment(Left),
Col::new().min_width(2).and_alignment(Right),
Col::new().min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::new().min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::new().min_width("Duration".len()).and_alignment(Right),
Col::new().min_width("Notes".len()).max_width(44).and_alignment(Left),
]);
tabs.feed(vec!["ID".into(), "Day".into(), "Start End".into(), "Duration".into(), "Notes".into()]);
tabs.feed(vec!["60000".into(), "Sun Oct 05, 2008".into(), "16:00:00 - 18:00:00".into(), "2:00:00".into(), LONG_NOTE.into()]);
tabs.feed(vec!["".into(), "".into(), "".into(), "2:00:00".into(), "".into()]);
tabs.feed(vec!["ID", "Day", "Start End", "Duration", "Notes"]);
tabs.feed(vec!["60000", "Sun Oct 05, 2008", "16:00:00 - 18:00:00", "2:00:00", LONG_NOTE]);
tabs.feed(vec!["", "", "", "2:00:00", ""]);
tabs.separator('-');
tabs.feed(vec!["".into(), "Total".into(), "".into(), "2:00:00".into()]);
tabs.feed(vec!["", "Total", "", "2:00:00"]);
assert_eq!(&tabs.print(false), " ID Day Start End Duration Notes
60000 Sun Oct 05, 2008 16:00:00 - 18:00:00 2:00:00 chatting with bob about upcoming task,
@ -343,17 +341,17 @@ Total 52:00:00
#[test]
fn test_text_output_note_with_line_breaks() {
let mut tabs = Tabulate::with_columns(vec![
Col::min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::min_width("Duration".len()).and_alignment(Right),
Col::min_width("Notes".len()).and_alignment(Left),
Col::new().min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::new().min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::new().min_width("Duration".len()).and_alignment(Right),
Col::new().min_width("Notes".len()).and_alignment(Left),
]);
tabs.feed(vec!["Day".into(), "Start End".into(), "Duration".into(), "Notes".into()]);
tabs.feed(vec!["Sun Oct 05, 2008".into(), "16:00:00 - 18:00:00".into(), "2:00:00".into(), "first line\nand a second line".into()]);
tabs.feed(vec!["".into(), "".into(), "2:00:00".into(), "".into()]);
tabs.separator('-');
tabs.feed(vec!["Total".into(), "".into(), "2:00:00".into(), "".into()]);
tabs.feed(vec!["Day", "Start End", "Duration", "Notes"]);
tabs.feed(vec!["Sun Oct 05, 2008", "16:00:00 - 18:00:00", "2:00:00", "first line\nand a second line"]);
tabs.feed(vec!["", "", "2:00:00", ""]);
tabs.separator('-');
tabs.feed(vec!["Total", "", "2:00:00", ""]);
assert_eq!(&tabs.print(false), "\
Day Start End Duration Notes
@ -368,17 +366,17 @@ Total 2:00:00
#[test]
fn note_with_accents() {
let mut tabs = Tabulate::with_columns(vec![
Col::min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::min_width("Duration".len()).and_alignment(Right),
Col::min_width("Notes".len()).and_alignment(Left),
Col::new().min_width("Fri Oct 03, 2008 ".len()).and_alignment(Left),
Col::new().min_width("12:00:00 - 14:00:00 ".len()).and_alignment(Left),
Col::new().min_width("Duration".len()).and_alignment(Right),
Col::new().min_width("Notes".len()).and_alignment(Left),
]);
tabs.feed(vec!["Day".into(), "Start End".into(), "Duration".into(), "Notes".into()]);
tabs.feed(vec!["Sun Oct 05, 2008".into(), "16:00:00 - 18:00:00".into(), "2:00:00".into(), "quiúbole".into()]);
tabs.feed(vec!["".into(), "".into(), "2:00:00".into(), "".into()]);
tabs.feed(vec!["Day", "Start End", "Duration", "Notes"]);
tabs.feed(vec!["Sun Oct 05, 2008", "16:00:00 - 18:00:00", "2:00:00", "quiúbole"]);
tabs.feed(vec!["", "", "2:00:00", ""]);
tabs.separator('-');
tabs.feed(vec!["Total".into(), "".into(), "2:00:00".into(), "".into()]);
tabs.feed(vec!["Total", "", "2:00:00", ""]);
assert_eq!(&tabs.print(false), "\
Day Start End Duration Notes
@ -395,11 +393,11 @@ Total 2:00:00
Col::new()
]);
tabs.feed(vec!["Hola".into()]);
tabs.feed(vec!["Hola"]);
tabs.separator(' ');
tabs.feed(vec!["adiós".into()]);
tabs.feed(vec!["adiós"]);
tabs.separator('-');
tabs.feed(vec!["ta güeno".into()]);
tabs.feed(vec!["ta güeno"]);
assert_eq!(&tabs.print(false), "\
Hola
@ -419,8 +417,8 @@ ta güeno
Col::new(),
]);
tabs.feed(vec!["foo".into(), "key".into()]);
tabs.feed(vec!["key".into(), "foo".into()]);
tabs.feed(vec!["foo", "key"]);
tabs.feed(vec!["key", "foo"]);
assert_eq!(tabs.print(true), format!("\
foo key