Skip to main content

rustledger_plugin/native/plugins/
leaf_only.rs

1//! Error on postings to non-leaf accounts.
2
3use crate::types::{DirectiveData, PluginError, PluginInput, PluginOutput};
4
5use super::super::NativePlugin;
6
7/// Plugin that errors when posting to non-leaf (parent) accounts.
8pub struct LeafOnlyPlugin;
9
10impl NativePlugin for LeafOnlyPlugin {
11    fn name(&self) -> &'static str {
12        "leafonly"
13    }
14
15    fn description(&self) -> &'static str {
16        "Error on postings to non-leaf accounts"
17    }
18
19    fn process(&self, input: PluginInput) -> PluginOutput {
20        use std::collections::HashSet;
21
22        // Collect all accounts used
23        let mut all_accounts: HashSet<String> = HashSet::new();
24        for wrapper in &input.directives {
25            if let DirectiveData::Transaction(txn) = &wrapper.data {
26                for posting in &txn.postings {
27                    all_accounts.insert(posting.account.clone());
28                }
29            }
30        }
31
32        // Find parent accounts (accounts that are prefixes of others)
33        let parent_accounts: HashSet<&String> = all_accounts
34            .iter()
35            .filter(|acc| {
36                all_accounts
37                    .iter()
38                    .any(|other| other != *acc && other.starts_with(&format!("{acc}:")))
39            })
40            .collect();
41
42        // Check for postings to parent accounts
43        let mut errors = Vec::new();
44        for wrapper in &input.directives {
45            if let DirectiveData::Transaction(txn) = &wrapper.data {
46                for posting in &txn.postings {
47                    if parent_accounts.contains(&posting.account) {
48                        errors.push(PluginError::error(format!(
49                            "Posting to non-leaf account '{}' - has child accounts",
50                            posting.account
51                        )));
52                    }
53                }
54            }
55        }
56
57        PluginOutput {
58            directives: input.directives,
59            errors,
60        }
61    }
62}