123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- use crate::data::{AccountName, Hoard, Transaction, TransactionRef};
- #[derive(Clone, Copy, Default)]
- #[allow(unused)]
- pub enum Align {
- #[default]
- Left,
- Centre,
- Right,
- }
- #[derive(Clone, Default)]
- pub struct Column {
- pub align: Align,
- pub left_border: bool,
- pub right_border: bool,
- }
- pub enum Row {
- Line,
- Data(Vec<String>),
- }
- pub 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!();
- }
- }
- pub fn show_transaction(_root: Option<&Hoard>, txn: &Transaction) {
- let bluestyle = console::Style::new().blue();
- // let greenstyle = console::Style::new().green();
- let yellowstyle = console::Style::new().yellow();
- let graystyle = console::Style::new().white().dim();
- println!(
- "{}: {}",
- bluestyle.apply_to(txn.datestamp),
- txn.title.as_deref().unwrap_or("")
- );
- for change in &txn.changes {
- println!(
- " - {}: {} {}",
- yellowstyle.apply_to(change.account),
- change.amount,
- graystyle.apply_to(change.unit)
- );
- }
- }
- #[derive(Clone, Copy, Default)]
- pub struct TransactionTable {}
- impl TransactionTable {
- fn make_columns(&self) -> Vec<Column> {
- 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()
- },
- ]
- }
- fn make_header_row(&self) -> Row {
- Row::Data(vec![
- "Date".into(),
- "Memo".into(),
- "Amount".into(),
- "Balance".into(),
- ])
- }
- fn format_txn(
- &self,
- root: Option<&Hoard>,
- account: AccountName,
- txn: &Transaction,
- ) -> Option<Row> {
- let Some(chg) = txn.change_for(account) else {
- return None;
- };
- let precision = root
- .and_then(|r| r.unit_spec(*chg.unit))
- .and_then(|v| v.precision)
- .unwrap_or(2) as usize;
- Some(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()),
- ]))
- }
- pub fn show<'d>(
- self,
- root: Option<&Hoard>,
- account: AccountName,
- txns: impl Iterator<Item = &'d Transaction>,
- ) {
- show_table(
- self.make_columns(),
- vec![self.make_header_row(), Row::Line]
- .into_iter()
- .chain(txns.filter_map(|txn| self.format_txn(root, account, txn))),
- )
- }
- pub fn show_refs<'d>(
- self,
- root: Option<&Hoard>,
- account: AccountName,
- txns: impl Iterator<Item = &'d TransactionRef>,
- ) {
- show_table(
- self.make_columns(),
- vec![self.make_header_row(), Row::Line]
- .into_iter()
- .chain(txns.filter_map(|txn| self.format_txn(root, account, &txn.borrow()))),
- )
- }
- }
|