okane_core/report/
process.rs1use std::borrow::Borrow;
2use std::path::PathBuf;
3
4use crate::{load, syntax};
5
6use super::balance::Balance;
7use super::book_keeping::{self, BookKeepError};
8use super::context::ReportContext;
9use super::error::{self, ReportError};
10use super::price_db::PriceRepositoryBuilder;
11use super::query::Ledger;
12use super::transaction::Transaction;
13
14#[derive(Debug, Default)]
16pub struct ProcessOptions {
18 pub price_db_path: Option<PathBuf>,
20}
21
22pub fn process<'ctx, L, F>(
26 ctx: &mut ReportContext<'ctx>,
27 loader: L,
28 options: &ProcessOptions,
29) -> Result<Ledger<'ctx>, ReportError>
30where
31 L: Borrow<load::Loader<F>>,
32 F: load::FileSystem,
33{
34 let mut accum = ProcessAccumulator::new();
35 loader.borrow().load(|path, pctx, entry| {
36 accum.process(ctx, entry).map_err(|berr| {
37 ReportError::BookKeep(
38 berr,
39 error::ErrorContext::new(
40 loader.borrow().error_style().clone(),
41 path.to_owned(),
42 pctx,
43 ),
44 )
45 })
46 })?;
47 if let Some(price_db_path) = options.price_db_path.as_deref() {
48 accum
49 .price_repos
50 .load_price_db(ctx, loader.borrow().filesystem(), price_db_path)?;
51 }
52 Ok(Ledger {
53 arena: ctx.arena,
54 transactions: accum.txns,
55 date_sorted_txns: None,
56 raw_balance: accum.balance,
57 price_repos: accum.price_repos.build(),
58 })
59}
60
61struct ProcessAccumulator<'ctx> {
62 balance: Balance<'ctx>,
63 txns: Vec<Transaction<'ctx>>,
64 price_repos: PriceRepositoryBuilder<'ctx>,
65}
66
67impl<'ctx> ProcessAccumulator<'ctx> {
68 fn new() -> Self {
69 Self {
70 balance: Balance::default(),
71 txns: Vec::new(),
72 price_repos: PriceRepositoryBuilder::default(),
73 }
74 }
75
76 fn process(
77 &mut self,
78 ctx: &mut ReportContext<'ctx>,
79 entry: &syntax::tracked::LedgerEntry,
80 ) -> Result<(), BookKeepError> {
81 match entry {
82 syntax::LedgerEntry::Txn(txn) => {
83 self.txns.push(book_keeping::add_transaction(
84 ctx,
85 &mut self.price_repos,
86 &mut self.balance,
87 txn,
88 )?);
89 Ok(())
90 }
91 syntax::LedgerEntry::Account(account) => process_account(ctx, account),
92 syntax::LedgerEntry::Commodity(commodity) => process_commodity(ctx, commodity),
93 _ => Ok(()),
94 }
95 }
96}
97
98pub fn process_account<'ctx>(
99 ctx: &mut ReportContext<'ctx>,
100 account: &syntax::AccountDeclaration<'_>,
101) -> Result<(), BookKeepError> {
102 let canonical = ctx.accounts.ensure(&account.name);
103 for ad in &account.details {
104 if let syntax::AccountDetail::Alias(alias) = ad {
105 ctx.accounts
106 .insert_alias(alias, canonical)
107 .map_err(|_| BookKeepError::InvalidAccountAlias(alias.to_string()))?;
108 }
109 }
110 Ok(())
111}
112
113fn process_commodity<'ctx>(
114 ctx: &mut ReportContext<'ctx>,
115 commodity: &syntax::CommodityDeclaration<'_>,
116) -> Result<(), BookKeepError> {
117 let canonical = ctx.commodities.ensure(&commodity.name);
118 for cd in &commodity.details {
119 match cd {
120 syntax::CommodityDetail::Alias(alias) => {
121 ctx.commodities
122 .insert_alias(alias, canonical)
123 .map_err(|_| BookKeepError::InvalidCommodityAlias(alias.to_string()))?;
124 }
125 syntax::CommodityDetail::Format(format_amount) => {
126 ctx.commodities.set_format(canonical, format_amount.value);
127 }
128 _ => {}
129 }
130 }
131 Ok(())
132}