123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139 |
- use console::Style;
- use crate::data::{
- AccountName, Decimal, Root,
- ledger::{Change, Transaction},
- spec::AccountSpec,
- };
- #[derive(Clone, Copy, Default)]
- enum Align {
- #[default]
- Left,
- Centre,
- Right,
- }
- #[derive(Default)]
- struct Column {
- pub align: Align,
- pub left_border: bool,
- pub right_border: bool,
- // pub contents: Box<dyn Iterator<Item = dyn Display>>,
- }
- enum Row {
- Line,
- Data(Vec<String>),
- }
- fn show_table(cols: Vec<Column>, rows: impl Iterator<Item = Row>) {
- let mut min_widths = vec![0usize; cols.len()];
- let table_data = rows.collect::<Vec<_>>();
- for row in &table_data {
- let Row::Data(row) = &row else { continue };
- for (idx, rc) in row.iter().enumerate() {
- min_widths[idx] = min_widths[idx].max(console::measure_text_width(rc.as_str()));
- }
- }
- for row in table_data {
- match row {
- Row::Line => {
- for (idx, col) in min_widths.iter().enumerate() {
- if cols[idx].left_border {
- print!("{}", boxy::Char::cross(boxy::Weight::Normal));
- }
- for _ in 0..=*col {
- print!("{}", boxy::Char::horizontal(boxy::Weight::Normal));
- }
- if cols[idx].right_border {
- print!("{}", boxy::Char::cross(boxy::Weight::Normal));
- }
- }
- }
- Row::Data(row) => {
- for (idx, col) in row.into_iter().enumerate() {
- if cols[idx].left_border {
- print!("{}", boxy::Char::vertical(boxy::Weight::Normal));
- }
- match cols[idx].align {
- Align::Left => print!("{col:<width$}", width = min_widths[idx] + 1),
- Align::Centre => print!("{col:^width$}", width = min_widths[idx] + 1),
- Align::Right => print!("{col:>width$}", width = min_widths[idx] + 1),
- }
- if cols[idx].right_border {
- print!("{}", boxy::Char::vertical(boxy::Weight::Normal));
- }
- }
- }
- }
- println!();
- }
- }
- #[derive(Clone, Copy, Default)]
- pub struct TransactionTable {}
- impl TransactionTable {
- pub fn show<'d>(
- self,
- root: Option<&Root>,
- account: AccountName,
- txns: impl Iterator<Item = &'d Transaction>,
- ) {
- show_table(
- vec![
- // datestamp
- Column {
- align: Align::Left,
- right_border: true,
- ..Default::default()
- },
- // Memo
- Column {
- align: Align::Left,
- ..Default::default()
- },
- // Amount
- Column {
- align: Align::Right,
- ..Default::default()
- },
- // Balance
- Column {
- align: Align::Right,
- left_border: true,
- ..Default::default()
- },
- ],
- vec![
- Row::Data(vec![
- "Date".into(),
- "Memo".into(),
- "Amount".into(),
- "Balance".into(),
- ]),
- Row::Line,
- ]
- .into_iter()
- .chain(
- txns.filter_map(|txn| txn.change_for(account).map(|chg| (txn, chg)))
- .map(|(txn, chg)| {
- let precision =
- root.and_then(|r| r.unit_spec(*chg.unit)).and_then(|v| v.precision).unwrap_or(2) as usize;
- Row::Data(vec![
- txn.datestamp.to_string(),
- txn.title.clone().unwrap_or_else(String::new),
- format!("{:.precision$}", chg.amount),
- chg.balance
- .as_deref()
- .map(|b| format!("{:.precision$}", b))
- .unwrap_or(String::new()),
- ])
- }),
- ),
- )
- }
- }
|