Skip to main content

rex_app/
conn.rs

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}