Răsfoiți Sursa

Introduce TransactionRef and massage infer to work correctly via.

Kestrel 2 zile în urmă
părinte
comite
1fb2a0dc34
6 a modificat fișierele cu 148 adăugiri și 89 ștergeri
  1. 4 3
      src/check.rs
  2. 11 11
      src/cmd.rs
  3. 12 10
      src/cmd/infer.rs
  4. 23 26
      src/data.rs
  5. 25 6
      src/data/format.rs
  6. 73 33
      src/show.rs

+ 4 - 3
src/check.rs

@@ -18,7 +18,7 @@ pub enum CheckLevel {
 fn check_equal_sum(root: &data::Hoard) -> Result<(), DataError> {
     log::trace!("Checking for equal sums in monounit ledger entries...");
 
-    for le in root.all_ledger_data() {
+    for le in root.all_raw_ledger_data() {
         let Some(tx) = le.as_transaction() else {
             continue;
         };
@@ -54,7 +54,7 @@ fn check_equal_sum(root: &data::Hoard) -> Result<(), DataError> {
 fn check_precision(root: &data::Hoard) -> Result<(), DataError> {
     log::trace!("Checking for precision errors in ledger entries...");
 
-    for le in root.all_ledger_data() {
+    for le in root.all_raw_ledger_data() {
         let Some(tx) = le.as_transaction() else {
             continue;
         };
@@ -114,7 +114,8 @@ fn check_balances(root: &data::Hoard) -> Result<(), DataError> {
         };
 
         let mut running_balance = BTreeMap::<data::UnitName, Spanned<data::Decimal>>::new();
-        for txn in ledger {
+        for txn_ref in ledger {
+            let txn = txn_ref.borrow();
             let change = txn.change_for(account).unwrap();
             let bal = running_balance
                 .entry(*change.unit)

+ 11 - 11
src/cmd.rs

@@ -114,7 +114,7 @@ impl Command {
 
                 let tt = show::TransactionTable::default();
                 if let Some(ld) = data.ledger_data_for(aname) {
-                    tt.show(Some(&data), aname, ld.iter());
+                    tt.show_refs(Some(&data), aname, ld.iter());
                 } else {
                     log::error!("account not found!");
                 }
@@ -124,7 +124,7 @@ impl Command {
 
                 if !skip_ids {
                     let mut used_ids = HashSet::new();
-                    for le in data.all_ledger_data() {
+                    for le in data.all_raw_ledger_data() {
                         let Some(txn) = le.as_transaction() else {
                             continue;
                         };
@@ -135,7 +135,7 @@ impl Command {
                     }
 
                     // assign IDs if to transactions lacking them
-                    for le in data.all_ledger_data_mut() {
+                    for le in data.all_raw_ledger_data_mut() {
                         let Some(txn) = le.as_transaction_mut() else {
                             continue;
                         };
@@ -151,7 +151,7 @@ impl Command {
                     }
                 }
 
-                data::format_ledger(&mut fsdata, &data, data.all_ledger_data().iter())?;
+                data::format_ledger(&mut fsdata, &data, data.all_raw_ledger_data().iter())?;
             }
             Self::Import {
                 account,
@@ -179,13 +179,13 @@ impl Command {
 
                     let new_data = imported
                         .into_iter()
-                        .map(|txn| data::LedgerEntry::Transaction(txn))
+                        .map(|txn| data::RawLedgerEntry::Transaction(txn))
                         .collect_vec();
 
                     data::format_ledger(
                         &mut fsdata,
                         &data,
-                        data.ledger_data_from(new_source).chain(new_data.iter()),
+                        data.raw_ledger_data_from(new_source).chain(new_data.iter()),
                     )?;
                 } else {
                     log::info!("No target specified, showing new data on stdout.");
@@ -197,7 +197,7 @@ impl Command {
                 let mut data = load_data(&mut fsdata, inv, check::CheckLevel::WellFormed)?;
 
                 if infer::do_inference(&mut data)? {
-                    data::format_ledger(&mut fsdata, &data, data.all_ledger_data().iter())?;
+                    data::format_entire_ledger(&mut fsdata, &data)?;
                 }
             }
             Self::Match { account } => {
@@ -208,16 +208,16 @@ impl Command {
                     return Ok(());
                 };
 
-                let txs = acc_data
+                let txns = acc_data
                     .iter()
-                    .filter(|v| v.get_annotation("id").is_some())
+                    .filter(|v| v.borrow().get_annotation("id").is_some())
                     .sorted()
                     .collect_vec();
 
-                show::TransactionTable::default().show(
+                show::TransactionTable::default().show_refs(
                     Some(&data),
                     *account,
-                    txs.into_iter()
+                    txns.into_iter()
                 );
             }
         }

+ 12 - 10
src/cmd/infer.rs

@@ -1,16 +1,17 @@
 use crate::data::Hoard;
 use crate::show::show_transaction;
 
+fn input_loop() -> anyhow::Result<bool> {
+    Ok(true)
+}
+
 pub fn do_inference(data: &mut Hoard) -> anyhow::Result<bool> {
     let placeholder = data.spec_root().placeholder_account;
 
-    let num_txns = data.ledger_data_for(placeholder).unwrap().len();
-
     let mut any_changed = false;
 
-    for idx in 0..num_txns {
-        let txns = data.ledger_data_for(placeholder).unwrap();
-        let txn = &txns[idx];
+    for txn_ref in data.ledger_data_for(placeholder).unwrap() {
+        let txn = txn_ref.borrow();
         let txn_title = txn.title.clone();
 
         log::trace!("Considering transaction");
@@ -31,8 +32,9 @@ pub fn do_inference(data: &mut Hoard) -> anyhow::Result<bool> {
 
         drop(chg);
 
-        for other_txn in other_txns {
-            if other_txn.title.is_none() || other_txn.title != txn_title || other_txn == txn {
+        for other_txn_ref in other_txns {
+            let other_txn = other_txn_ref.borrow();
+            if other_txn.title.is_none() || other_txn.title != txn_title || *other_txn == *txn {
                 continue;
             }
 
@@ -49,7 +51,7 @@ pub fn do_inference(data: &mut Hoard) -> anyhow::Result<bool> {
                 txn.title
             );
 
-            show_transaction(None, txn);
+            show_transaction(None, &txn);
 
             println!(
                 "Candidate account: {} (Y/n)",
@@ -64,9 +66,9 @@ pub fn do_inference(data: &mut Hoard) -> anyhow::Result<bool> {
                         console::style(new_account).green()
                     );
 
-                    drop(chg);
+                    drop(txn);
 
-                    data.ledger_data_for_mut(placeholder).unwrap()[idx]
+                    txn_ref.borrow_mut()
                         .changes
                         .iter_mut()
                         .for_each(|c| {

+ 23 - 26
src/data.rs

@@ -13,7 +13,7 @@ mod format;
 mod parse;
 pub mod spec;
 
-pub use format::format_ledger;
+pub use format::{format_ledger,format_entire_ledger};
 pub use parse::parse_ledger;
 
 pub struct UnitTag;
@@ -115,7 +115,7 @@ impl Transaction {
 pub type TransactionRef = std::rc::Rc<std::cell::RefCell<Transaction>>;
 
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
-enum RawLedgerEntry {
+pub enum RawLedgerEntry {
     Transaction(Transaction),
     Comment(Spanned<String>),
 }
@@ -157,16 +157,16 @@ impl LedgerEntry {
         }
     }
 
-    pub fn as_transaction_mut(&mut self) -> Option<&mut Transaction> {
+    /*pub fn as_transaction_mut(&mut self) -> Option<&mut Transaction> {
         match self {
             Self::Transaction(tx) => Some(tx),
             _ => None,
         }
-    }
+    }*/
 
     pub fn span(&self) -> io::Span {
         match self {
-            Self::Transaction(ts) => ts.source.unwrap_or_default(),
+            Self::Transaction(ts) => ts.borrow().source.unwrap_or_default(),
             Self::Comment(c) => c.span(),
         }
     }
@@ -181,7 +181,7 @@ pub struct Hoard {
 
     raw_ledger_data: Vec<RawLedgerEntry>,
 
-    account_ledger_data: HashMap<AccountName, Vec<Transaction>>,
+    account_ledger_data: HashMap<AccountName, Vec<TransactionRef>>,
 }
 
 impl Hoard {
@@ -288,41 +288,38 @@ impl Hoard {
 
     fn preprocess_ledger_data(&mut self) {
         for entry in &self.raw_ledger_data {
-            let RawLedgerEntry::Transaction(tx) = &entry else {
-                continue;
+            let tx = match entry {
+                RawLedgerEntry::Transaction(tx) => tx,
+                RawLedgerEntry::Comment(c) => {
+                    self.comments.push(c.clone());
+                    continue
+                }
             };
+
+            let txn_ref = std::rc::Rc::new(std::cell::RefCell::new(tx.clone()));
             for bal in &tx.changes {
                 self.account_ledger_data
                     .entry(*bal.account)
                     .or_default()
-                    .push(tx.clone());
+                    .push(txn_ref.clone());
             }
         }
     }
 
-    pub fn all_ledger_data(&self) -> &[LedgerEntry] {
-        self.ledger_data.as_slice()
+    pub fn all_raw_ledger_data(&self) -> &[RawLedgerEntry] {
+        self.raw_ledger_data.as_slice()
     }
 
-    pub fn all_ledger_data_mut(&mut self) -> &mut [LedgerEntry] {
-        self.ledger_data.as_mut_slice()
+    pub fn all_raw_ledger_data_mut(&mut self) -> &mut [RawLedgerEntry] {
+        self.raw_ledger_data.as_mut_slice()
     }
 
-    pub fn ledger_data_for(&self, aname: AccountName) -> Option<&[Transaction]> {
+    pub fn ledger_data_for(&self, aname: AccountName) -> Option<&[TransactionRef]> {
         self.account_ledger_data.get(&aname).map(Vec::as_slice)
     }
 
-    pub fn ledger_data_for_mut(
-        &mut self,
-        aname: AccountName,
-    ) -> Option<&mut [Transaction]> {
-        self.account_ledger_data
-            .get_mut(&aname)
-            .map(Vec::as_mut_slice)
-    }
-
-    pub fn ledger_data_from(&self, source: io::SourceFile) -> impl Iterator<Item = &LedgerEntry> {
-        self.all_ledger_data()
+    pub fn raw_ledger_data_from(&self, source: io::SourceFile) -> impl Iterator<Item = &RawLedgerEntry> {
+        self.all_raw_ledger_data()
             .iter()
             .filter(move |le| le.span().context == Some(source))
     }
@@ -347,7 +344,7 @@ impl Hoard {
         let mut running = HashMap::<UnitName, Decimal>::new();
 
         for le in self.ledger_data_for(aname)? {
-            for b in &le.changes {
+            for b in &le.borrow().changes {
                 if *b.account != aname {
                     continue;
                 }

+ 25 - 6
src/data/format.rs

@@ -1,11 +1,11 @@
-use std::collections::BTreeMap;
+use std::collections::{BTreeMap,BTreeSet};
 
 use itertools::Itertools;
 
 use crate::data::Hoard;
 use crate::io::{SourceFile, Span, Spanned};
 
-use super::{LedgerEntry, Transaction};
+use super::{RawLedgerEntry, Transaction, TransactionRef};
 
 fn format_transaction(
     target: &mut dyn std::io::Write,
@@ -70,9 +70,9 @@ fn format_comment(target: &mut dyn std::io::Write, c: &Spanned<String>) -> std::
 pub fn format_ledger<'l>(
     fsdata: &mut crate::io::FilesystemData,
     root: &Hoard,
-    entries: impl Iterator<Item = &'l LedgerEntry>,
+    entries: impl Iterator<Item = &'l RawLedgerEntry>,
 ) -> std::io::Result<()> {
-    let mut ordering = BTreeMap::<Option<SourceFile>, BTreeMap<Span, Vec<&LedgerEntry>>>::new();
+    let mut ordering = BTreeMap::<Option<SourceFile>, BTreeMap<Span, Vec<&RawLedgerEntry>>>::new();
 
     entries.for_each(|e| {
         ordering
@@ -104,10 +104,10 @@ pub fn format_ledger<'l>(
             les.sort_by_key(|le| le.as_transaction().map(|tx| tx.datestamp));
             for le in les {
                 match le {
-                    LedgerEntry::Transaction(tx) => {
+                    RawLedgerEntry::Transaction(tx) => {
                         format_transaction(&mut outfile, root, tx, padding)?
                     }
-                    LedgerEntry::Comment(c) => format_comment(&mut outfile, c)?,
+                    RawLedgerEntry::Comment(c) => format_comment(&mut outfile, c)?,
                 }
             }
         }
@@ -116,3 +116,22 @@ pub fn format_ledger<'l>(
     }
     Ok(())
 }
+
+pub fn format_entire_ledger<'l>(
+    fsdata: &mut crate::io::FilesystemData,
+    root: &Hoard
+) -> std::io::Result<()> {
+    let mut entries = BTreeSet::<TransactionRef>::new();
+
+    for acc in root.account_names() {
+        let Some(acc_data) = root.ledger_data_for(acc) else { continue };
+        for txn_ref in acc_data {
+            entries.insert(txn_ref.clone());
+        }
+    }
+
+    let raw_data = entries.into_iter().map(|v| RawLedgerEntry::Transaction(v.borrow().clone())).chain(
+        root.comments.iter().map(|v| RawLedgerEntry::Comment(v.clone()))).collect_vec();
+
+    format_ledger(fsdata, root, raw_data.iter())
+}

+ 73 - 33
src/show.rs

@@ -1,4 +1,4 @@
-use crate::data::{AccountName, Hoard, Transaction};
+use crate::data::{AccountName, Hoard, Transaction, TransactionRef};
 
 #[derive(Clone, Copy, Default)]
 #[allow(unused)]
@@ -91,6 +91,59 @@ pub fn show_transaction(_root: Option<&Hoard>, txn: &Transaction) {
 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>,
@@ -98,42 +151,15 @@ impl TransactionTable {
         txns: impl Iterator<Item = &'d Transaction>,
     ) {
         show_table(
+            self.make_columns(),
             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()
-                },
-            ],
-            vec![
-                Row::Data(vec![
-                    "Date".into(),
-                    "Memo".into(),
-                    "Amount".into(),
-                    "Balance".into(),
-                ]),
+                self.make_header_row(),
                 Row::Line,
             ]
             .into_iter()
             .chain(
-                txns.filter_map(|txn| txn.change_for(account).map(|chg| (txn, chg)))
+                txns.filter_map(|txn| self.format_txn(root, account, txn))
+                /*txns.filter_map(|txn| txn.change_for(account).map(|chg| (txn, chg)))
                     .map(|(txn, chg)| {
                         let precision = root
                             .and_then(|r| r.unit_spec(*chg.unit))
@@ -148,7 +174,21 @@ impl TransactionTable {
                                 .map(|b| format!("{:.precision$}", b))
                                 .unwrap_or(String::new()),
                         ])
-                    }),
+                    }),*/
+            ),
+        )
+    }
+
+    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()))
             ),
         )
     }