rustledger_plugin/native/plugins/
auto_accounts.rs1use crate::types::{
4 DirectiveData, DirectiveWrapper, OpenData, PluginInput, PluginOp, PluginOutput,
5};
6
7use super::super::NativePlugin;
8
9pub struct AutoAccountsPlugin;
11
12impl NativePlugin for AutoAccountsPlugin {
13 fn name(&self) -> &'static str {
14 "auto_accounts"
15 }
16
17 fn description(&self) -> &'static str {
18 "Auto-generate Open directives for used accounts"
19 }
20
21 fn is_synth(&self) -> bool {
25 true
26 }
27
28 fn process(&self, input: PluginInput) -> PluginOutput {
29 use std::collections::{HashMap, HashSet};
30
31 let mut opened_accounts: HashSet<String> = HashSet::new();
32 let mut account_first_use: HashMap<String, String> = HashMap::new(); for wrapper in &input.directives {
37 match &wrapper.data {
38 DirectiveData::Open(data) => {
39 opened_accounts.insert(data.account.clone());
40 }
41 DirectiveData::Transaction(txn) => {
42 for posting in &txn.postings {
43 account_first_use
44 .entry(posting.account.clone())
45 .and_modify(|existing| {
46 if wrapper.date < *existing {
47 existing.clone_from(&wrapper.date);
48 }
49 })
50 .or_insert_with(|| wrapper.date.clone());
51 }
52 }
53 DirectiveData::Balance(data) => {
54 account_first_use
55 .entry(data.account.clone())
56 .and_modify(|existing| {
57 if wrapper.date < *existing {
58 existing.clone_from(&wrapper.date);
59 }
60 })
61 .or_insert_with(|| wrapper.date.clone());
62 }
63 DirectiveData::Pad(data) => {
64 account_first_use
65 .entry(data.account.clone())
66 .and_modify(|existing| {
67 if wrapper.date < *existing {
68 existing.clone_from(&wrapper.date);
69 }
70 })
71 .or_insert_with(|| wrapper.date.clone());
72 account_first_use
73 .entry(data.source_account.clone())
74 .and_modify(|existing| {
75 if wrapper.date < *existing {
76 existing.clone_from(&wrapper.date);
77 }
78 })
79 .or_insert_with(|| wrapper.date.clone());
80 }
81 _ => {}
82 }
83 }
84
85 let mut accounts_to_open: Vec<_> = account_first_use
88 .iter()
89 .filter(|(account, _)| !opened_accounts.contains(*account))
90 .collect();
91 accounts_to_open.sort_by_key(|(account, _)| *account);
92
93 let mut ops: Vec<PluginOp> = (0..input.directives.len()).map(PluginOp::Keep).collect();
95
96 for (index, (account, date)) in accounts_to_open.into_iter().enumerate() {
98 ops.push(PluginOp::Insert(DirectiveWrapper {
99 directive_type: "open".to_string(),
100 date: date.clone(),
101 filename: Some("<auto_accounts>".to_string()),
102 lineno: Some(index as u32), data: DirectiveData::Open(OpenData {
104 account: account.clone(),
105 currencies: vec![],
106 booking: None,
107 metadata: vec![],
108 }),
109 }));
110 }
111
112 PluginOutput {
115 ops,
116 errors: Vec::new(),
117 }
118 }
119}