rustledger_plugin/native/plugins/
coherent_cost.rs1use crate::types::{DirectiveData, PluginError, PluginInput, PluginOp, PluginOutput};
4
5use super::super::NativePlugin;
6
7pub struct CoherentCostPlugin;
16
17impl NativePlugin for CoherentCostPlugin {
18 fn name(&self) -> &'static str {
19 "coherent_cost"
20 }
21
22 fn description(&self) -> &'static str {
23 "Enforce consistent cost tracking per currency"
24 }
25
26 fn process(&self, input: PluginInput) -> PluginOutput {
27 use std::collections::HashSet;
28
29 let mut currencies_with_cost: HashSet<&str> = HashSet::new();
32 let mut currencies_with_price_only: HashSet<&str> = HashSet::new();
34
35 for wrapper in &input.directives {
36 if let DirectiveData::Transaction(txn) = &wrapper.data {
37 for posting in &txn.postings {
38 if let Some(units) = &posting.units {
39 let currency = units.currency.as_str();
40
41 if posting.cost.is_some() {
43 currencies_with_cost.insert(currency);
44 } else if posting.price.is_some() {
45 currencies_with_price_only.insert(currency);
47 }
48 }
49 }
50 }
51 }
52
53 let mut inconsistent: Vec<_> = currencies_with_cost
56 .intersection(¤cies_with_price_only)
57 .copied()
58 .collect();
59 inconsistent.sort_unstable();
60
61 let errors: Vec<_> = inconsistent
62 .into_iter()
63 .map(|currency| {
64 PluginError::error(format!(
65 "Currency '{currency}' is used with both cost and price-only notation - this may cause inconsistencies"
66 ))
67 })
68 .collect();
69
70 PluginOutput {
71 ops: (0..input.directives.len()).map(PluginOp::Keep).collect(),
72 errors,
73 }
74 }
75}