use std::path::PathBuf;
use clap::{Args, ValueEnum};
use retiretui_engine::plan::Plan;
use retiretui_engine::project::{Projection, YearRow, project};
use retiretui_tui::table::{Column, align, display_dollars, year_figures};
#[derive(Args)]
pub struct ProjectArgs {
pub plan: PathBuf,
#[arg(long, value_enum, default_value_t = OutputFormat::Table)]
pub format: OutputFormat,
#[arg(long)]
pub nominal: bool,
#[arg(long)]
pub by_account: bool,
#[arg(long)]
pub tax_dir: Vec<PathBuf>,
}
#[derive(Clone, Copy, PartialEq, Eq, ValueEnum)]
pub enum OutputFormat {
Table,
Json,
}
pub fn run(args: &ProjectArgs) -> anyhow::Result<()> {
let tables = super::load_tables(&args.tax_dir)?;
let plan = super::load_validated_plan(&args.plan, &tables)?;
let projection = project(&plan, &tables);
match args.format {
OutputFormat::Json => println!("{}", serde_json::to_string_pretty(&projection)?),
OutputFormat::Table => print!("{}", render_table(&plan, &projection, args)),
}
Ok(())
}
fn render_table(plan: &Plan, projection: &Projection, args: &ProjectArgs) -> String {
let columns = balance_columns(plan, args.by_account);
let mut header = vec![
"year".to_owned(),
"age".to_owned(),
"income".to_owned(),
"expense".to_owned(),
"tax".to_owned(),
"wd".to_owned(),
];
header.extend(columns.iter().map(|column| match column {
Column::Account(id) => id.clone(),
Column::Class(class) => class.as_str().to_owned(),
}));
header.push("net".to_owned());
let rows: Vec<Vec<String>> = projection
.years
.iter()
.map(|row| format_row(plan, row, &columns, args.nominal))
.collect();
align(&header, &rows)
}
fn balance_columns(plan: &Plan, by_account: bool) -> Vec<Column> {
if by_account {
return plan
.accounts
.iter()
.map(|account| Column::Account(account.id.clone()))
.collect();
}
retiretui_tui::table::present_classes(plan)
.into_iter()
.map(Column::Class)
.collect()
}
fn format_row(plan: &Plan, row: &YearRow, columns: &[Column], nominal: bool) -> Vec<String> {
let mut cells = vec![
row.year.to_string(),
retiretui_tui::table::ages_text(plan, row),
];
cells.extend(
year_figures(row, columns)
.into_iter()
.map(|amount| display_dollars(amount, row.deflator, nominal)),
);
cells
}