1#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct DraftSplit {
17 pub account_id: String,
18 pub commodity_id: String,
19 pub value_num: i64,
20 pub value_denom: i64,
21 pub tags: Vec<DraftTag>,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26pub struct DraftTag {
27 pub name: String,
28 pub value: String,
29}
30
31#[derive(Debug, Clone, Default, PartialEq, Eq)]
35pub struct TransactionDraft {
36 pub note: Option<String>,
37 pub date: Option<String>,
38 pub splits: Vec<DraftSplit>,
39 pub tags: Vec<DraftTag>,
40}
41
42impl TransactionDraft {
43 #[must_use]
44 pub fn new() -> Self {
45 Self::default()
46 }
47
48 pub fn set_note(&mut self, note: String) {
49 self.note = Some(note);
50 }
51
52 pub fn set_date(&mut self, date: String) {
53 self.date = Some(date);
54 }
55
56 #[must_use]
59 pub fn split_count(&self) -> usize {
60 self.splits.len()
61 }
62
63 pub fn add_split(&mut self, split: DraftSplit) -> usize {
66 self.splits.push(split);
67 self.splits.len() - 1
68 }
69
70 pub fn add_tag(&mut self, tag: DraftTag) {
71 self.tags.push(tag);
72 }
73
74 pub fn add_split_tag(&mut self, index: usize, tag: DraftTag) -> bool {
77 match self.splits.get_mut(index) {
78 Some(split) => {
79 split.tags.push(tag);
80 true
81 }
82 None => false,
83 }
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use super::*;
90
91 fn split() -> DraftSplit {
92 DraftSplit {
93 account_id: "a".to_string(),
94 commodity_id: "c".to_string(),
95 value_num: 1,
96 value_denom: 1,
97 tags: Vec::new(),
98 }
99 }
100
101 #[test]
102 fn split_count_predicts_next_add_split_index() {
103 let mut draft = TransactionDraft::new();
104 assert_eq!(draft.split_count(), 0);
105 assert_eq!(draft.add_split(split()), 0);
106 assert_eq!(draft.split_count(), 1);
107 assert_eq!(draft.add_split(split()), 1);
108 assert_eq!(draft.split_count(), 2);
109 }
110
111 #[test]
112 fn add_split_tag_targets_only_the_indexed_split() {
113 let mut draft = TransactionDraft::new();
114 let first = draft.add_split(split());
115 let second = draft.add_split(split());
116
117 assert!(draft.add_split_tag(
118 first,
119 DraftTag {
120 name: "memo".to_string(),
121 value: "lunch".to_string(),
122 }
123 ));
124
125 assert_eq!(draft.splits[first].tags.len(), 1);
126 assert_eq!(draft.splits[first].tags[0].name, "memo");
127 assert!(draft.splits[second].tags.is_empty());
128 }
129
130 #[test]
131 fn add_split_tag_rejects_out_of_range_handle() {
132 let mut draft = TransactionDraft::new();
133 draft.add_split(split());
134 assert!(!draft.add_split_tag(
135 7,
136 DraftTag {
137 name: "k".to_string(),
138 value: "v".to_string(),
139 }
140 ));
141 }
142}