1mod amount;
3mod auto_cat;
4mod category;
5mod date;
6mod items;
7mod mapping;
8mod row_col;
9mod transaction;
10
11pub use amount::{Amount, AmountFormat};
12pub use auto_cat::{AutoCat, AutoCatUpdates, AutoCats};
13pub use category::{Categories, Category, CategoryUpdates};
14pub use date::Date;
15pub(crate) use date::{DateCanBeEmptyStr, DateFromOpt, DateFromOptStr};
16pub(crate) use items::Item;
17pub(crate) use mapping::Mapping;
18pub(crate) use row_col::RowCol;
19use serde::{Deserialize, Serialize};
20pub use transaction::{Transaction, TransactionColumn, TransactionUpdates, Transactions};
21
22#[derive(Default, Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
24#[serde(rename_all = "snake_case")]
25pub struct TillerData {
26 pub(crate) transactions: Transactions,
29 pub(crate) categories: Categories,
31 pub(crate) auto_cats: AutoCats,
33}
34
35impl TillerData {
36 pub(crate) fn has_formulas(&self) -> bool {
38 !self.transactions.formulas().is_empty()
39 || !self.categories.formulas().is_empty()
40 || !self.auto_cats.formulas().is_empty()
41 }
42
43 pub(crate) fn has_original_order_gaps(&self) -> bool {
48 if Self::check_gaps(self.transactions.data().iter().map(|t| t.original_order)) {
50 return true;
51 }
52 if Self::check_gaps(self.categories.data().iter().map(|c| c.original_order)) {
54 return true;
55 }
56 if Self::check_gaps(self.auto_cats.data().iter().map(|c| c.original_order)) {
58 return true;
59 }
60 false
61 }
62
63 fn check_gaps(orders: impl Iterator<Item = Option<u64>>) -> bool {
65 let mut orders: Vec<u64> = orders.flatten().collect();
66
67 if orders.is_empty() {
68 return false;
69 }
70
71 orders.sort();
72
73 for (i, &order) in orders.iter().enumerate() {
74 if order != i as u64 {
75 return true;
76 }
77 }
78
79 false
80 }
81}