accounting_core/utils/
validation.rs1use crate::traits::*;
4use crate::types::*;
5use bigdecimal::BigDecimal;
6
7pub fn validate_positive_amount(amount: &BigDecimal) -> LedgerResult<()> {
9 if *amount <= BigDecimal::from(0) {
10 Err(LedgerError::Validation(
11 "Amount must be positive".to_string(),
12 ))
13 } else {
14 Ok(())
15 }
16}
17
18pub fn validate_account_id(account_id: &str) -> LedgerResult<()> {
20 if account_id.trim().is_empty() {
21 return Err(LedgerError::Validation(
22 "Account ID cannot be empty".to_string(),
23 ));
24 }
25
26 if account_id.len() > 50 {
27 return Err(LedgerError::Validation(
28 "Account ID cannot exceed 50 characters".to_string(),
29 ));
30 }
31
32 if !account_id
34 .chars()
35 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
36 {
37 return Err(LedgerError::Validation(
38 "Account ID can only contain alphanumeric characters, dashes, and underscores"
39 .to_string(),
40 ));
41 }
42
43 Ok(())
44}
45
46pub fn validate_account_name(name: &str) -> LedgerResult<()> {
48 if name.trim().is_empty() {
49 return Err(LedgerError::Validation(
50 "Account name cannot be empty".to_string(),
51 ));
52 }
53
54 if name.len() > 100 {
55 return Err(LedgerError::Validation(
56 "Account name cannot exceed 100 characters".to_string(),
57 ));
58 }
59
60 Ok(())
61}
62
63pub fn validate_transaction_description(description: &str) -> LedgerResult<()> {
65 if description.trim().is_empty() {
66 return Err(LedgerError::Validation(
67 "Transaction description cannot be empty".to_string(),
68 ));
69 }
70
71 if description.len() > 500 {
72 return Err(LedgerError::Validation(
73 "Transaction description cannot exceed 500 characters".to_string(),
74 ));
75 }
76
77 Ok(())
78}
79
80pub struct EnhancedTransactionValidator;
82
83impl TransactionValidator for EnhancedTransactionValidator {
84 fn validate_transaction(&self, transaction: &Transaction) -> LedgerResult<()> {
85 transaction.validate()?;
87
88 validate_transaction_description(&transaction.description)?;
90
91 for entry in &transaction.entries {
93 validate_account_id(&entry.account_id)?;
94 validate_positive_amount(&entry.amount)?;
95 }
96
97 let mut account_entry_combinations = std::collections::HashSet::new();
99 for entry in &transaction.entries {
100 let combination = (&entry.account_id, &entry.entry_type);
101 if !account_entry_combinations.insert(combination) {
102 return Err(LedgerError::Validation(format!(
103 "Account '{}' appears multiple times with the same entry type in transaction",
104 entry.account_id
105 )));
106 }
107 }
108
109 Ok(())
110 }
111
112 fn validate_account_references(&self, _transaction: &Transaction) -> LedgerResult<()> {
113 Ok(())
116 }
117}
118
119pub struct EnhancedAccountValidator;
121
122impl AccountValidator for EnhancedAccountValidator {
123 fn validate_account(&self, account: &Account) -> LedgerResult<()> {
124 validate_account_id(&account.id)?;
125 validate_account_name(&account.name)?;
126
127 Ok(())
129 }
130
131 fn validate_account_deletion(&self, _account_id: &str) -> LedgerResult<()> {
132 Ok(())
135 }
136}