|
@@ -1,130 +1,12 @@
|
|
|
-use itertools::Itertools;
|
|
|
-
|
|
|
mod check;
|
|
|
+mod cmd;
|
|
|
mod data;
|
|
|
mod show;
|
|
|
mod import;
|
|
|
|
|
|
-#[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,
|
|
|
- Import { account: String, from: std::path::PathBuf },
|
|
|
-}
|
|
|
-
|
|
|
-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());
|
|
|
-
|
|
|
- let tt = show::TransactionTable::default();
|
|
|
- if let Some(ld) = data.ledger_data_for(aname) {
|
|
|
- tt.show(&data, aname, ld.iter().map(data::Spanned::as_ref));
|
|
|
- } else {
|
|
|
- log::error!("account not found!");
|
|
|
- }
|
|
|
- }
|
|
|
- Self::Reformat => {
|
|
|
- let data = load_data(&mut fsdata, inv)?;
|
|
|
-
|
|
|
- data::ledger::print_ledger(&data, data.all_ledger_data().iter());
|
|
|
- }
|
|
|
- Self::Import { account, from } => {
|
|
|
- let data = load_data(&mut fsdata, inv)?;
|
|
|
-
|
|
|
- let aname = account.into();
|
|
|
- let Some(aspec) = data.account_spec(aname) else {
|
|
|
- todo!()
|
|
|
- };
|
|
|
-
|
|
|
- let imported = import::import_from(aspec, aname, from.as_path()).unwrap();
|
|
|
-
|
|
|
- let tt = show::TransactionTable::default();
|
|
|
- tt.show(&data, aname, imported.iter());
|
|
|
- }
|
|
|
- }
|
|
|
- Ok(())
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
fn main() -> anyhow::Result<()> {
|
|
|
use clap::Parser;
|
|
|
- let inv = Invocation::parse();
|
|
|
+ let inv = cmd::Invocation::parse();
|
|
|
|
|
|
pretty_env_logger::formatted_builder()
|
|
|
.filter_level(match inv.verbose {
|