rustledger_plugin/native/plugins/
coherent_cost.rs1use crate::types::{DirectiveData, PluginError, PluginInput, PluginOutput};
4
5use super::super::NativePlugin;
6
7pub struct CoherentCostPlugin;
12
13impl NativePlugin for CoherentCostPlugin {
14 fn name(&self) -> &'static str {
15 "coherent_cost"
16 }
17
18 fn description(&self) -> &'static str {
19 "Enforce cost OR price (not both) consistency"
20 }
21
22 fn process(&self, input: PluginInput) -> PluginOutput {
23 use std::collections::{HashMap, HashSet};
24
25 let mut currencies_with_cost: HashSet<String> = HashSet::new();
27 let mut currencies_with_price: HashSet<String> = HashSet::new();
28 let mut first_use: HashMap<String, (String, String)> = HashMap::new(); for wrapper in &input.directives {
31 if let DirectiveData::Transaction(txn) = &wrapper.data {
32 for posting in &txn.postings {
33 if let Some(units) = &posting.units {
34 let currency = &units.currency;
35
36 if posting.cost.is_some() && !currencies_with_cost.contains(currency) {
37 currencies_with_cost.insert(currency.clone());
38 first_use
39 .entry(currency.clone())
40 .or_insert(("cost".to_string(), wrapper.date.clone()));
41 }
42
43 if posting.price.is_some() && !currencies_with_price.contains(currency) {
44 currencies_with_price.insert(currency.clone());
45 first_use
46 .entry(currency.clone())
47 .or_insert(("price".to_string(), wrapper.date.clone()));
48 }
49 }
50 }
51 }
52 }
53
54 let mut errors = Vec::new();
56 for currency in currencies_with_cost.intersection(¤cies_with_price) {
57 errors.push(PluginError::error(format!(
58 "Currency '{currency}' is used with both cost and price notation - this may cause inconsistencies"
59 )));
60 }
61
62 PluginOutput {
63 directives: input.directives,
64 errors,
65 }
66 }
67}