123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134 |
- use itertools::Itertools;
- mod check;
- mod data;
- #[derive(clap::Parser)]
- struct Invocation {
- /// path to treasure file
- #[clap(long, short, env = "TREASURE_FILE")]
- file: std::path::PathBuf,
- /// verbosity of output messages
- #[clap(long, short, action = clap::ArgAction::Count)]
- verbose: u8,
- #[clap(subcommand)]
- cmd: Command,
- }
- #[derive(clap::Subcommand)]
- enum Command {
- Summarize,
- Ledger { account: String },
- Reformat,
- }
- fn load_data(
- fsdata: &mut data::FilesystemData,
- invocation: &Invocation,
- ) -> anyhow::Result<data::Root> {
- match data::Root::load(fsdata, &invocation.file) {
- Ok(data) => Ok(data),
- Err(data::DataError::IOError(ioerror)) => Err(ioerror.into()),
- Err(data::DataError::Report(report)) => {
- report.eprint(fsdata)?;
- Err(anyhow::anyhow!("Error reported"))
- }
- Err(data::DataError::Validation(verr)) => Err(anyhow::anyhow!("Validation error: {verr}")),
- }
- }
- impl Command {
- fn run(&self, inv: &Invocation) -> anyhow::Result<()> {
- let mut fsdata = data::FilesystemData::default();
- match self {
- Self::Summarize => {
- let data = load_data(&mut fsdata, inv)?;
- let positive = console::Style::new().green();
- let negative = console::Style::new().red();
- let neutral = console::Style::new();
- let Some(maxlen) = data.account_names().map(|v| v.len()).max() else {
- return Ok(());
- };
- for shortname in data.account_names().sorted_by_key(|an| an.as_str()) {
- if shortname.as_ref() == "initial" {
- continue;
- }
- if let Some(balances) = data.balance(shortname) {
- let balances = balances
- .into_iter()
- .filter(|(_, b)| !b.is_zero())
- .sorted()
- .map(|(u, b)| {
- (
- u,
- if b.is_sign_positive() {
- positive.apply_to(b)
- } else if b.is_sign_negative() {
- negative.apply_to(b)
- } else {
- neutral.apply_to(b)
- },
- )
- });
- println!(
- "{shortname:maxlen$} {}",
- balances.map(|(u, b)| format!("{b:8} {u:5}")).join(" ")
- );
- }
- }
- }
- Self::Ledger { account } => {
- let data = load_data(&mut fsdata, inv)?;
- let aname = data::AccountName::new(account.as_str());
- if let Some(ld) = data.ledger_data_for(aname) {
- for le in ld {
- let Some((acc_bal, other_bal)) = le.split_balances(aname) else {
- continue;
- };
- log::info!(
- "{:?}: {} {:?}",
- le.datestamp,
- acc_bal.amount,
- other_bal.map(|b| b.account).collect::<Vec<_>>()
- );
- }
- } else {
- log::info!("account not found. data: {data:?}");
- }
- // let data = data::Root::load(&mut fsdata, &inv.file);
- // data.account(account)?;
- }
- Self::Reformat => {
- let data = load_data(&mut fsdata, inv)?;
- data::ledger::print_ledger(&data, data.all_ledger_data().iter());
- }
- }
- Ok(())
- }
- }
- fn main() -> anyhow::Result<()> {
- use clap::Parser;
- let inv = Invocation::parse();
- pretty_env_logger::formatted_builder()
- .filter_level(match inv.verbose {
- 0 => log::LevelFilter::Info,
- 1 => log::LevelFilter::Debug,
- _ => log::LevelFilter::Trace,
- })
- .init();
- inv.cmd.run(&inv)
- }
|