Browse Source

reformat command now adds ID annotations by default.

Kestrel 2 weeks ago
parent
commit
14853d79f0
5 changed files with 68 additions and 10 deletions
  1. 1 0
      Cargo.toml
  2. 43 9
      src/cmd.rs
  3. 4 0
      src/data.rs
  4. 16 0
      src/data/ledger.rs
  5. 4 1
      testdata/ledger

+ 1 - 0
Cargo.toml

@@ -10,6 +10,7 @@ anyhow = { version = "1" }
 stringstore = { version = "0.1.5", features = ["serde"] }
 rust_decimal = { version = "1.37" }
 itertools = { version = "0.14" }
+nanoid = { version = "0.4.0" }
 
 # i/o dependencies
 serde = { version = "1.0", features = ["derive"] }

+ 43 - 9
src/cmd.rs

@@ -1,3 +1,5 @@
+use std::collections::HashSet;
+
 use itertools::Itertools;
 
 use crate::{check::CheckLevel, data, import::import_from, io, show};
@@ -39,13 +41,19 @@ pub enum Command {
     Ledger {
         account: String,
     },
-    Reformat,
+    Reformat {
+        #[clap(long)]
+        /// do not add ID annotations to transactions lacking them
+        skip_ids: bool,
+    },
     Import {
         account: String,
         from: std::path::PathBuf,
         target: Option<std::path::PathBuf>,
     },
-    Dedup,
+    Match {
+        account: data::AccountName
+    },
 }
 
 fn summarize(data: &data::Root) {
@@ -108,8 +116,31 @@ impl Command {
                     log::error!("account not found!");
                 }
             }
-            Self::Reformat => {
-                let data = load_data(&mut fsdata, inv, CheckLevel::WellFormed)?;
+            Self::Reformat { skip_ids } => {
+                let mut data = load_data(&mut fsdata, inv, CheckLevel::WellFormed)?;
+
+                if !skip_ids {
+                    let mut used_ids = HashSet::new();
+                    for le in data.all_ledger_data() {
+                        let Some(txn) = le.as_transaction() else { continue };
+                        let Some(id) = txn.get_annotation("id") else { continue };
+                        used_ids.insert(id.to_owned());
+                    }
+
+                    // assign IDs if to transactions lacking them
+                    for le in data.all_ledger_data_mut() {
+                        let Some(txn) = le.as_transaction_mut() else { continue };
+                        if !txn.get_annotation("id").is_some() {
+                            // generated unique ID
+                            let mut id = nanoid::nanoid!(10);
+                            while used_ids.contains(&id) {
+                                id = nanoid::nanoid!(10);
+                            }
+                            // assign ID
+                            txn.annotations.push(format!("id:{id}"));
+                        }
+                    }
+                }
 
                 data::ledger::print_ledger(&mut fsdata, &data, data.all_ledger_data().iter())?;
             }
@@ -151,14 +182,17 @@ impl Command {
                     tt.show(Some(&data), aname, imported.iter());
                 }
             }
-            Self::Dedup => {
-                /*
+            Self::Match { account } => {
                 let data = load_data(&mut fsdata, inv, CheckLevel::WellFormed)?;
 
-                let is_dup = |txn1: &data::ledger::Transaction, txn2: &data::ledger::Transaction| {
+                let Some(acc_data) = data.ledger_data_for(*account) else {
+                    log::error!("No ledger data available for account {account}");
+                    return Ok(())
+                };
+
+
 
-                };*/
-                todo!()
+                show::TransactionTable::default().show(Some(&data), *account, acc_data.iter().map(|d| &d.0));
             }
         }
         Ok(())

+ 4 - 0
src/data.rs

@@ -331,6 +331,10 @@ impl Root {
         self.ledger_data.as_slice()
     }
 
+    pub fn all_ledger_data_mut(&mut self) -> &mut [ledger::LedgerEntry] {
+        self.ledger_data.as_mut_slice()
+    }
+
     pub fn ledger_data_for(&self, aname: AccountName) -> Option<&[Spanned<ledger::Transaction>]> {
         self.account_ledger_data.get(&aname).map(Vec::as_slice)
     }

+ 16 - 0
src/data/ledger.rs

@@ -58,6 +58,15 @@ impl Transaction {
         let uniq = it.next()?;
         it.next().is_none().then_some(*uniq.unit)
     }
+
+    pub fn get_annotation(&self, label: &str) -> Option<&str> {
+        for anno in self.annotations.iter() {
+            if let Some(body) = anno.strip_prefix(label) {
+                return Some(body)
+            }
+        }
+        None
+    }
 }
 
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -74,6 +83,13 @@ impl LedgerEntry {
         }
     }
 
+    pub fn as_transaction_mut(&mut self) -> Option<&mut Spanned<Transaction>> {
+        match self {
+            Self::Transaction(tx) => Some(tx),
+            _ => None,
+        }
+    }
+
     pub fn span(&self) -> super::Span {
         match self {
             Self::Transaction(ts) => ts.span(),

+ 4 - 1
testdata/ledger

@@ -1,18 +1,21 @@
 # vim: set filetype=hoard :
-
 2001-01-05: initial balance
+    [id:KPjRU1qu1V]
  - initial    : -400.00 CAD
  - chequing   :  400.00 CAD
 
 2001-01-07: transfer to savings
+    [id:N4nIfle5SO]
  - chequing   : -300.00 = 100.00 CAD
  - savings    :  300.00 CAD
 
 2001-02-08: test for unusual account name
+    [id:b2anD72LVb]
  - initial    : -4.00 USD
  - a.b/c      :  4.00 USD
 
 2001-02-09: currency conversion
+    [id:2eB6QkDWBe]
  - savings    : -100.00 CAD
  - savings_usd:  80.00 USD