1use anyhow::{Error, Result};
2use chrono::NaiveDate;
3use diesel::{Connection, SqliteConnection};
4pub use rex_db::models::FetchNature;
5use rex_db::models::{Balance, FullTx, NewSearch, NewTx, Tag, Tx, TxMethod};
6use rex_db::{Cache, ConnCache, get_connection, get_connection_no_migrations};
7use std::collections::{HashMap, HashSet};
8
9use crate::modifier::{
10 activity_delete_tx, activity_edit_tx, activity_new_tx, activity_search_tx,
11 activity_swap_position, add_new_tx, add_new_tx_methods, delete_tx,
12};
13use crate::ui_helper::{Autofiller, Stepper, Verifier};
14use crate::utils::month_name_to_num;
15use crate::views::{
16 ActivityView, ChartView, SearchView, SummaryView, TxViewGroup, get_activity_view,
17 get_chart_view, get_search_txs, get_summary, get_txs,
18};
19
20#[must_use]
21pub fn get_conn(location: &str) -> DbConn {
22 DbConn::new(location)
23}
24
25#[must_use]
26pub fn get_conn_old(location: &str) -> DbConn {
27 DbConn::new_no_migrations(location)
28}
29
30pub struct MutDbConn<'a> {
31 conn: &'a mut SqliteConnection,
32 cache: &'a Cache,
33}
34
35impl<'a> MutDbConn<'a> {
36 pub fn new(conn: &'a mut SqliteConnection, cache: &'a Cache) -> Self {
37 MutDbConn { conn, cache }
38 }
39
40 pub fn verify(&mut self) -> Verifier<'_> {
41 let db_conn = MutDbConn::new(self.conn, self.cache);
42 Verifier::new(db_conn)
43 }
44}
45
46impl ConnCache for MutDbConn<'_> {
47 fn conn(&mut self) -> &mut SqliteConnection {
48 self.conn
49 }
50
51 fn cache(&self) -> &Cache {
52 self.cache
53 }
54}
55
56pub struct DbConn {
57 pub conn: SqliteConnection,
58 pub cache: Cache,
59}
60
61impl ConnCache for DbConn {
62 fn conn(&mut self) -> &mut SqliteConnection {
63 &mut self.conn
64 }
65
66 fn cache(&self) -> &Cache {
67 &self.cache
68 }
69}
70
71impl DbConn {
72 #[must_use]
73 pub fn new(db_url: &str) -> Self {
74 let conn = get_connection(db_url);
75
76 let mut to_return = DbConn {
77 conn,
78 cache: Cache {
79 tags: HashMap::new(),
80 tx_methods: HashMap::new(),
81 txs: None,
82 details: HashSet::new(),
83 },
84 };
85
86 to_return.reload_methods();
87 to_return.reload_tags();
88 to_return.reload_details();
89
90 to_return
91 }
92
93 #[must_use]
94 pub fn new_no_migrations(db_url: &str) -> Self {
95 let conn = get_connection_no_migrations(db_url);
96 DbConn {
97 conn,
98 cache: Cache {
99 tags: HashMap::new(),
100 tx_methods: HashMap::new(),
101 txs: None,
102 details: HashSet::new(),
103 },
104 }
105 }
106
107 pub(crate) fn reload_methods(&mut self) {
108 let tx_methods = TxMethod::get_all(self)
109 .unwrap()
110 .into_iter()
111 .map(|t| (t.id, t))
112 .collect();
113
114 self.cache.tx_methods = tx_methods;
115 }
116
117 pub(crate) fn reload_tags(&mut self) {
118 let tags = Tag::get_all(self)
119 .unwrap()
120 .into_iter()
121 .map(|t| (t.id, t))
122 .collect();
123
124 self.cache.tags = tags;
125 }
126
127 pub(crate) fn reload_details(&mut self) {
128 self.cache.details = Tx::get_all_details(self).unwrap().into_iter().collect();
129 }
130
131 pub fn add_new_tx(&mut self, tx: NewTx, tags: &str) -> Result<()> {
132 self.conn.transaction::<_, Error, _>(|conn| {
133 let mut db_conn = MutDbConn::new(conn, &self.cache);
134
135 let new_tags = add_new_tx(tx.clone(), tags, None, &mut db_conn)?;
136
137 activity_new_tx(&tx, tags, &mut db_conn)?;
138 self.cache.new_tags(new_tags);
139
140 Ok(())
141 })?;
142
143 Ok(())
144 }
145
146 pub fn delete_tx(&mut self, tx: &FullTx) -> Result<()> {
147 self.conn.transaction::<_, Error, _>(|conn| {
148 let mut db_conn = MutDbConn::new(conn, &self.cache);
149
150 delete_tx(tx, &mut db_conn)?;
151 activity_delete_tx(tx, &mut db_conn)?;
152
153 Ok(())
154 })?;
155
156 Ok(())
157 }
158
159 pub fn edit_tx(&mut self, old_tx: &FullTx, new_tx: NewTx, tags: &str) -> Result<()> {
160 self.conn.transaction::<_, Error, _>(|conn| {
161 let mut db_conn = MutDbConn::new(conn, &self.cache);
162
163 let old_tx_id = old_tx.id;
164 delete_tx(old_tx, &mut db_conn)?;
165
166 let new_tags = add_new_tx(new_tx.clone(), tags, Some(old_tx_id), &mut db_conn)?;
167
168 activity_edit_tx(old_tx, &new_tx, tags, &mut db_conn)?;
169
170 self.cache.new_tags(new_tags);
171
172 Ok(())
173 })?;
174
175 Ok(())
176 }
177
178 pub fn add_new_methods(&mut self, method_list: &Vec<String>) -> Result<()> {
179 self.conn.transaction::<_, Error, _>(|conn| {
180 let mut db_conn = MutDbConn::new(conn, &self.cache);
181
182 let new_methods = add_new_tx_methods(method_list, &mut db_conn)?;
183
184 self.cache.new_tx_methods(new_methods);
185
186 Ok(())
187 })?;
188
189 Ok(())
190 }
191
192 pub fn fetch_tx_with_id(&mut self, id: i32) -> Result<FullTx> {
193 let tx = FullTx::get_tx_by_id(id, self)?;
194
195 Ok(tx)
196 }
197
198 pub fn fetch_txs_with_str<'a>(
199 &mut self,
200 month: &'a str,
201 year: &'a str,
202 nature: FetchNature,
203 ) -> Result<TxViewGroup> {
204 let result = self.conn.transaction::<TxViewGroup, Error, _>(|conn| {
205 let mut db_conn = MutDbConn::new(conn, &self.cache);
206
207 let year_num = year.parse::<i32>().unwrap();
208 let month_num = month_name_to_num(month);
209
210 let date = NaiveDate::from_ymd_opt(year_num, month_num, 1).unwrap();
211
212 get_txs(date, nature, &mut db_conn)
213 })?;
214
215 Ok(result)
216 }
217
218 pub fn fetch_txs_with_date(
219 &mut self,
220 date: NaiveDate,
221 nature: FetchNature,
222 ) -> Result<TxViewGroup> {
223 let result = self.conn.transaction::<TxViewGroup, Error, _>(|conn| {
224 let mut db_conn = MutDbConn::new(conn, &self.cache);
225
226 get_txs(date, nature, &mut db_conn)
227 })?;
228
229 Ok(result)
230 }
231
232 pub fn search_txs(&mut self, search: NewSearch) -> Result<SearchView> {
233 let result = self.conn.transaction::<SearchView, Error, _>(|conn| {
234 let mut db_conn = MutDbConn::new(conn, &self.cache);
235
236 let search_view = get_search_txs(&search, &mut db_conn)?;
237
238 activity_search_tx(&search, &mut db_conn)?;
239
240 Ok(search_view)
241 })?;
242
243 Ok(result)
244 }
245
246 pub fn get_summary_with_str<'a>(
247 &mut self,
248 month: &'a str,
249 year: &'a str,
250 nature: FetchNature,
251 ) -> Result<SummaryView> {
252 let (summary, txs) = self
253 .conn
254 .transaction::<(SummaryView, Option<HashMap<i32, Vec<FullTx>>>), Error, _>(|conn| {
255 let mut db_conn = MutDbConn::new(conn, &self.cache);
256
257 let year_num = year.parse::<i32>().unwrap();
258 let month_num = month_name_to_num(month);
259
260 let date = NaiveDate::from_ymd_opt(year_num, month_num, 1).unwrap();
261
262 get_summary(date, nature, &mut db_conn)
263 })?;
264
265 if let Some(txs) = txs {
266 self.cache.set_txs(txs);
267 }
268
269 Ok(summary)
270 }
271
272 pub fn get_chart_view_with_str<'a>(
273 &mut self,
274 month: &'a str,
275 year: &'a str,
276 nature: FetchNature,
277 ) -> Result<ChartView> {
278 let result = self.conn.transaction::<ChartView, Error, _>(|conn| {
279 let mut db_conn = MutDbConn::new(conn, &self.cache);
280
281 let year_num = year.parse::<i32>().unwrap();
282 let month_num = month_name_to_num(month);
283
284 let date = NaiveDate::from_ymd_opt(year_num, month_num, 1).unwrap();
285
286 let tx_view = get_txs(date, nature, &mut db_conn)?;
287
288 let chart_view = get_chart_view(tx_view);
289
290 Ok(chart_view)
291 })?;
292
293 Ok(result)
294 }
295
296 pub fn get_activity_view_with_str<'a>(
297 &mut self,
298 month: &'a str,
299 year: &'a str,
300 ) -> Result<ActivityView> {
301 let result = self.conn.transaction::<ActivityView, Error, _>(|conn| {
302 let mut db_conn = MutDbConn::new(conn, &self.cache);
303
304 let year_num = year.parse::<i32>().unwrap();
305 let month_num = month_name_to_num(month);
306
307 let date = NaiveDate::from_ymd_opt(year_num, month_num, 1).unwrap();
308
309 let activity_view = get_activity_view(date, &mut db_conn)?;
310
311 Ok(activity_view)
312 })?;
313
314 Ok(result)
315 }
316
317 pub fn swap_tx_position(
318 &mut self,
319 index_1: usize,
320 index_2: usize,
321 tx_view_group: &mut TxViewGroup,
322 ) -> Result<bool> {
323 let result = self.conn.transaction::<bool, Error, _>(|conn| {
324 let mut db_conn = MutDbConn::new(conn, &self.cache);
325
326 let result = tx_view_group.switch_tx_index(index_1, index_2, &mut db_conn)?;
327
328 let tx_1 = tx_view_group.get_tx(index_1);
329 let tx_2 = tx_view_group.get_tx(index_2);
330
331 activity_swap_position(tx_1, tx_2, &mut db_conn)?;
332
333 Ok(result)
334 })?;
335
336 Ok(result)
337 }
338
339 pub fn rename_tx_method(&mut self, old_name: &str, new_name: &str) -> Result<()> {
340 let id = self.conn.transaction::<i32, Error, _>(|conn| {
341 let mut db_conn = MutDbConn::new(conn, &self.cache);
342
343 let target_method = db_conn.cache().get_method_by_name(old_name)?.id;
344
345 TxMethod::rename(target_method, new_name, &mut db_conn)?;
346
347 Ok(target_method)
348 })?;
349
350 let method = self.cache.tx_methods.get_mut(&id).unwrap();
351 method.name = new_name.to_string();
352
353 Ok(())
354 }
355
356 pub fn set_new_tx_method_positions(&mut self, new_format: &[String]) -> Result<()> {
357 let mut new_method_positions = Vec::new();
358
359 for (ongoing_position, method) in new_format.iter().enumerate() {
360 let mut target_method = self.cache.get_method_by_name(method)?.clone();
361 target_method.position = ongoing_position as i32;
362
363 new_method_positions.push(target_method);
364 }
365
366 self.conn.transaction::<_, Error, _>(|conn| {
367 let mut db_conn = MutDbConn::new(conn, &self.cache);
368
369 for method in &new_method_positions {
370 method.set_new_position(&mut db_conn)?;
371 }
372
373 Ok(())
374 })?;
375
376 self.cache.tx_methods.clear();
377 self.cache.new_tx_methods(new_method_positions);
378
379 Ok(())
380 }
381
382 #[must_use]
383 pub fn get_tx_methods(&self) -> &HashMap<i32, TxMethod> {
384 &self.cache.tx_methods
385 }
386
387 #[must_use]
388 pub fn get_tx_methods_sorted(&self) -> Vec<&TxMethod> {
389 self.cache.get_methods()
390 }
391
392 #[must_use]
393 pub fn get_tx_methods_cumulative(&self) -> Vec<String> {
394 let mut methods: Vec<String> = self
395 .get_tx_methods_sorted()
396 .iter()
397 .map(|m| m.name.clone())
398 .collect();
399
400 methods.push("Cumulative".to_string());
401 methods
402 }
403
404 #[must_use]
405 pub fn is_tx_method_empty(&self) -> bool {
406 self.cache.tx_methods.is_empty()
407 }
408
409 pub fn get_tx_method_by_name(&mut self, name: &str) -> Result<&TxMethod> {
410 self.cache.get_method_by_name(name)
411 }
412
413 pub fn get_final_balances(&mut self) -> Result<HashMap<i32, Balance>> {
414 Ok(Balance::get_final_balance(self)?)
415 }
416
417 pub fn autofill(&mut self) -> Autofiller<'_> {
418 let db_conn = MutDbConn::new(&mut self.conn, &self.cache);
419 Autofiller::new(db_conn)
420 }
421
422 pub fn verify(&mut self) -> Verifier<'_> {
423 let db_conn = MutDbConn::new(&mut self.conn, &self.cache);
424 Verifier::new(db_conn)
425 }
426
427 pub fn step(&mut self) -> Stepper<'_> {
428 let db_conn = MutDbConn::new(&mut self.conn, &self.cache);
429 Stepper::new(db_conn)
430 }
431}