use crate::kernel::balance::Balance;
use crate::kernel::report_item_selector::{BalanceSelector, RegisterSelector};
use crate::model::{RegisterEntry, RegisterPosting, Transaction, TxnRefs, TxnSet};
use itertools::Itertools;
use rust_decimal::Decimal;
use std::collections::HashMap;
use std::error::Error;
use std::io;
pub(crate) type RegisterReporterFn<W> =
fn(writer: &mut W, &RegisterEntry) -> Result<(), Box<dyn Error>>;
pub(crate) type TxnGroupByOp<'a> = Box<dyn Fn(&Transaction) -> String + 'a>;
pub(crate) fn balance_groups<T>(txns: &TxnRefs, group_by_op: TxnGroupByOp, ras: &T) -> Vec<Balance>
where
T: BalanceSelector + ?Sized,
{
txns.iter()
.cloned() .chunk_by(|txn| group_by_op(txn))
.into_iter()
.map(|(group_by_key, bal_grp_txns)| {
let txns = bal_grp_txns.collect();
let metadata = None;
let txn_set = TxnSet { metadata, txns };
Balance::from(&group_by_key, &txn_set, ras)
})
.filter(|bal| !bal.is_empty())
.sorted_by_key(|bal| bal.title.clone()) .collect()
}
pub(crate) fn register_engine<'a, W, T>(
txns: &'a TxnRefs,
ras: &T,
w: &mut W,
reporter: RegisterReporterFn<W>,
) -> Result<(), Box<dyn Error>>
where
W: io::Write + ?Sized,
T: RegisterSelector<'a> + ?Sized,
{
let mut register_engine: HashMap<String, Decimal> = HashMap::new();
for txn in txns {
let register_postings: Vec<_> = txn
.posts
.iter()
.map(|p| {
let key = p.acctn.get_full();
let running_total = *register_engine
.entry(key)
.and_modify(|v| {
*v += p.amount;
})
.or_insert(p.amount);
RegisterPosting {
post: p,
amount: running_total,
}
})
.collect();
let mut filt_postings: Vec<_> = register_postings
.iter()
.filter(|p| ras.eval(p))
.cloned()
.collect();
filt_postings.sort();
let register_entry = RegisterEntry {
txn,
posts: filt_postings,
};
reporter(w, ®ister_entry)?;
}
Ok(())
}