vz_tools_cli/
db.rs

1use std::{
2    fmt::{Display, Formatter},
3    fs,
4    path::PathBuf,
5};
6
7use anyhow::{anyhow, Result};
8use chrono::DateTime;
9use colored::Colorize;
10use rusqlite::{params, Connection};
11
12#[derive(Debug)]
13pub struct Tx {
14    date: String,
15    net_amount: f64,
16    dividend: f64,
17    tax: f64,
18    remainder: f64,
19}
20
21impl Tx {
22    pub fn new(date: String, net_amount: f64, dividend: f64, tax: f64, remainder: f64) -> Tx {
23        Tx {
24            date,
25            net_amount,
26            dividend,
27            tax,
28            remainder,
29        }
30    }
31}
32
33impl Display for Tx {
34    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
35        let date =
36            DateTime::parse_from_rfc3339(&self.date).expect("string to date conversion error");
37
38        write!(
39            f,
40            "| {0: <15} | {1:<15.2} | {2:<15.2} | {3:<15.2} | {4:<15.2}",
41            date.format("%d-%b-%y").to_string().bright_green(),
42            self.net_amount,
43            self.dividend,
44            self.tax,
45            self.remainder
46        )
47    }
48}
49
50pub fn db_connect() -> Result<Connection> {
51    let mut db_file: PathBuf = dirs::home_dir().ok_or(anyhow!("Couldn't get home directory"))?;
52    db_file.push(".vztools");
53
54    if !db_file.exists() {
55        println!("Creating db file: {}", db_file.to_str().unwrap());
56        fs::create_dir_all(&db_file)?;
57    }
58
59    db_file.push("db.db3");
60
61    let db_file_path = db_file
62        .to_str()
63        .expect("Couldn't convert db path to string");
64
65    let conn = Connection::open(db_file_path)?;
66
67    conn.execute(
68        "CREATE TABLE if not exists txs (
69          id    INTEGER PRIMARY KEY AUTOINCREMENT,
70          date TEXT NOT NULL,
71          net_amount REAL NOT NULL,
72          dividend REAL NOT NULL,
73          tax REAL NOT NULL,
74          remainder REAL NOT NULL
75      )",
76        (), // empty list of parameters.
77    )?;
78
79    return Ok(conn);
80}
81
82pub fn insert_tx(conn: &Connection, tx: &Tx) -> Result<()> {
83    conn.execute(
84        "INSERT INTO txs (date, net_amount, dividend, tax, remainder) VALUES (?1, ?2, ?3, ?4, ?5)",
85        params![tx.date, tx.net_amount, tx.dividend, tx.tax, tx.remainder],
86    )?;
87
88    return Ok(());
89}
90
91pub fn find_latest_tx(conn: &Connection) -> Result<Tx> {
92    let mut stmt = conn.prepare(
93        "SELECT id, date, net_amount, dividend, tax, remainder FROM txs ORDER BY id DESC LIMIT 1",
94    )?;
95    let person_iter = stmt.query_map([], |row| {
96        Ok(Tx {
97            date: row.get(1)?,
98            net_amount: row.get(2)?,
99            dividend: row.get(3)?,
100            tax: row.get(4)?,
101            remainder: row.get(5)?,
102        })
103    })?;
104
105    for person in person_iter {
106        return Ok(person?);
107    }
108
109    return Err(anyhow!("No tx found"));
110}
111
112pub fn find_all_tx(conn: &Connection) -> Result<Vec<Tx>> {
113    let mut stmt = conn.prepare(
114        "SELECT id, date, net_amount, dividend, tax, remainder FROM txs ORDER BY id DESC LIMIT 20",
115    )?;
116    let person_iter = stmt.query_map([], |row| {
117        Ok(Tx {
118            date: row.get(1)?,
119            net_amount: row.get(2)?,
120            dividend: row.get(3)?,
121            tax: row.get(4)?,
122            remainder: row.get(5)?,
123        })
124    })?;
125
126    let mut people = Vec::new();
127    for person in person_iter {
128        people.push(person?);
129    }
130
131    return Ok(people);
132}