|
@@ -0,0 +1,142 @@
|
|
|
+use std::collections::HashSet;
|
|
|
+
|
|
|
+use crate::prelude::*;
|
|
|
+
|
|
|
+use crate::{show, data::{AccountName, Hoard}};
|
|
|
+
|
|
|
+fn prompt_account(data: &Hoard) -> anyhow::Result<Option<AccountName>> {
|
|
|
+ println!("Enter new account:");
|
|
|
+
|
|
|
+ let mut acc = String::default();
|
|
|
+
|
|
|
+ let stdout = console::Term::stdout();
|
|
|
+
|
|
|
+ println!();
|
|
|
+
|
|
|
+ stdout.move_cursor_up(1)?;
|
|
|
+
|
|
|
+ loop {
|
|
|
+ stdout.clear_line()?;
|
|
|
+ stdout.write_line(format!("{}", console::style(&acc).yellow()).as_str())?;
|
|
|
+ stdout.move_cursor_up(1)?;
|
|
|
+ stdout.move_cursor_right(acc.len())?;
|
|
|
+
|
|
|
+ match console::Term::stdout().read_key()? {
|
|
|
+ console::Key::Backspace => {
|
|
|
+ acc.pop();
|
|
|
+ },
|
|
|
+ console::Key::Enter => {
|
|
|
+ if let Some(r) = data.account_names().find(|an| **an == *acc) {
|
|
|
+ return Ok(Some(r))
|
|
|
+ }
|
|
|
+ },
|
|
|
+ console::Key::Char(c) => {
|
|
|
+ if !c.is_control() {
|
|
|
+ acc.push(c);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ console::Key::Tab => {
|
|
|
+ if acc.is_empty() { continue }
|
|
|
+
|
|
|
+ let possibles = data.account_names().filter(|an| an.starts_with(acc.as_str())).collect::<Vec<_>>();
|
|
|
+
|
|
|
+ if possibles.is_empty() { continue }
|
|
|
+
|
|
|
+ let longest = possibles.iter().map(|s| s.len()).min().unwrap();
|
|
|
+
|
|
|
+ let mut longest_prefix = 0;
|
|
|
+ for plen in 1..=longest {
|
|
|
+ if possibles.iter().all(|p| p.starts_with(&possibles[0][0..plen])) {
|
|
|
+ longest_prefix = plen;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ acc.clear();
|
|
|
+ acc.push_str(&possibles[0][0..longest_prefix]);
|
|
|
+
|
|
|
+ stdout.clear_to_end_of_screen()?;
|
|
|
+
|
|
|
+ if possibles.len() == 1 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ stdout.move_cursor_down(1)?;
|
|
|
+ stdout.clear_line()?;
|
|
|
+
|
|
|
+ let mut line = String::new();
|
|
|
+ let len = possibles.len();
|
|
|
+ for possible in possibles.iter().take(5) {
|
|
|
+ line.push_str(format!("{} ", console::style(possible).white().dim()).as_str());
|
|
|
+ }
|
|
|
+ if len > 5 {
|
|
|
+ line.push_str("...");
|
|
|
+ }
|
|
|
+
|
|
|
+ stdout.write_line(line.as_str())?;
|
|
|
+ stdout.move_cursor_up(2)?;
|
|
|
+
|
|
|
+ },
|
|
|
+ console::Key::Escape => {
|
|
|
+ return Ok(None)
|
|
|
+ },
|
|
|
+ _key => (),
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+pub fn do_comb(data: &Hoard, account: AccountName) -> anyhow::Result<bool> {
|
|
|
+ let Some(txns) = data.ledger_data_for(account) else {
|
|
|
+ log::error!("No ledger data available for account {account}");
|
|
|
+ return Ok(false)
|
|
|
+ };
|
|
|
+
|
|
|
+ let mut aborted = false;
|
|
|
+ let mut changed = false;
|
|
|
+
|
|
|
+ let mut ignore = HashSet::<Option<String>>::default();
|
|
|
+
|
|
|
+ for txn_ref in txns {
|
|
|
+ let txn = txn_ref.borrow();
|
|
|
+ if txn.changes.len() != 2 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ if ignore.contains(&txn.title) {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ println!();
|
|
|
+ show::show_transaction(Some(&data), &txn);
|
|
|
+
|
|
|
+ println!();
|
|
|
+ println!("(I)gnore (S)kip all (E)dit (F)inish (A)bort: ");
|
|
|
+ let action = loop {
|
|
|
+ match console::Term::stdout().read_char().unwrap().to_lowercase().next() {
|
|
|
+ Some('s') => break 's',
|
|
|
+ Some('e') => break 'e',
|
|
|
+ Some('i') => break 'i',
|
|
|
+ Some('f') => break 'f',
|
|
|
+ Some('a') => break 'a',
|
|
|
+ _ => (),
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ if action == 'i' { () }
|
|
|
+ else if action == 's' {
|
|
|
+ ignore.insert(txn.title.clone());
|
|
|
+ } else if action == 'a' {
|
|
|
+ aborted = true;
|
|
|
+ break
|
|
|
+ } else if action == 'e' {
|
|
|
+ let Some(new_account) = prompt_account(data)? else { continue };
|
|
|
+ drop(txn);
|
|
|
+ let mut txn = txn_ref.borrow_mut();
|
|
|
+ txn.changes.iter_mut().find(|e| *e.account == account).iter_mut().for_each(|ch| *ch.account = new_account);
|
|
|
+ changed = true;
|
|
|
+ } else if action == 'f' {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Ok(!aborted && changed)
|
|
|
+}
|