From 877f64b33adf32cf8495d1364d184055aab8089c Mon Sep 17 00:00:00 2001
From: Joris
Date: Fri, 31 Jan 2025 23:30:30 +0100
Subject: Migrate to tokio_rusqlite

---
 src/controller/payments.rs |   7 ++-
 src/db/payments.rs         | 109 +++++++++++++++++++++++++++++++--------------
 src/model/frequency.rs     |   4 +-
 3 files changed, 84 insertions(+), 36 deletions(-)

diff --git a/src/controller/payments.rs b/src/controller/payments.rs
index 1ffa09f..3fa85a5 100644
--- a/src/controller/payments.rs
+++ b/src/controller/payments.rs
@@ -92,7 +92,9 @@ pub async fn create(
 
     match validation::payment::create(&form) {
         Some(create_payment) => {
-            match db::payments::create(&wallet.db_conn, create_payment.clone()).await {
+            match db::payments::create(&wallet.db_conn, create_payment.clone())
+                .await
+            {
                 Some(id) => {
                     let row = db::payments::get_row(
                         &wallet.db_conn,
@@ -234,7 +236,8 @@ pub async fn search_category(
     wallet: &Wallet,
     query: queries::PaymentCategory,
 ) -> Response<Full<Bytes>> {
-    match db::payments::search_category(&wallet.db_conn, query.payment_name).await
+    match db::payments::search_category(&wallet.db_conn, query.payment_name)
+        .await
     {
         Some(category_id) => utils::with_headers(
             Response::new(format!("{}", category_id).into()),
diff --git a/src/db/payments.rs b/src/db/payments.rs
index 540a006..4a6774c 100644
--- a/src/db/payments.rs
+++ b/src/db/payments.rs
@@ -1,6 +1,8 @@
 use std::collections::HashMap;
 use std::iter::FromIterator;
-use tokio_rusqlite::{Connection, Row, params_from_iter, types::ToSql, named_params};
+use tokio_rusqlite::{
+    named_params, params_from_iter, types::ToSql, Connection, Row,
+};
 
 use crate::db::utils;
 use crate::model::frequency::Frequency;
@@ -30,7 +32,7 @@ fn row_to_table(row: &Row) -> Result<payment::Table, rusqlite::Error> {
         category_name: row.get(4)?,
         category_color: row.get(5)?,
         date: row.get(6)?,
-        frequency: row.get(7)?
+        frequency: row.get(7)?,
     })
 }
 
@@ -50,7 +52,7 @@ fn row_to_form(row: &Row) -> Result<payment::Form, rusqlite::Error> {
         user_id: row.get(3)?,
         category_id: row.get(4)?,
         date: row.get(5)?,
-        frequency: row.get(6)?
+        frequency: row.get(6)?,
     })
 }
 
@@ -58,7 +60,7 @@ pub async fn count(
     conn: &Connection,
     payment_query: &queries::Payments,
 ) -> Count {
-    let mut query = format!(r#"
+    let mut query = r#"
         SELECT
             COUNT(*) AS count,
             SUM(payments.cost) AS total_cost
@@ -66,7 +68,8 @@ pub async fn count(
         INNER JOIN users ON users.id = payments.user_id
         INNER JOIN categories ON categories.id = payments.category_id
         WHERE payments.deleted_at IS NULL
-    "#);
+    "#
+    .to_string();
 
     let mut params = Vec::<Box<dyn ToSql + Send>>::new();
     complete_search_query(payment_query, &mut query, &mut params);
@@ -74,7 +77,8 @@ pub async fn count(
     let res = conn
         .call(move |conn| {
             let mut stmt = conn.prepare(&query)?;
-            let mut iter = stmt.query_map(params_from_iter(params), row_to_count)?;
+            let mut iter =
+                stmt.query_map(params_from_iter(params), row_to_count)?;
             utils::one(&mut iter)
         })
         .await;
@@ -96,7 +100,7 @@ pub async fn list_for_table(
     payment_query: &queries::Payments,
     per_page: i64,
 ) -> Vec<payment::Table> {
-    let mut query = format!(r#"
+    let mut query = r#"
         SELECT
             payments.id,
             payments.name,
@@ -110,7 +114,8 @@ pub async fn list_for_table(
         INNER JOIN users ON users.id = payments.user_id
         INNER JOIN categories ON categories.id = payments.category_id
         WHERE payments.deleted_at IS NULL
-    "#);
+    "#
+    .to_string();
 
     let mut params = Vec::<Box<dyn ToSql + Send>>::new();
     complete_search_query(payment_query, &mut query, &mut params);
@@ -141,35 +146,62 @@ pub async fn list_for_table(
 fn complete_search_query(
     q: &queries::Payments,
     query: &mut String,
-    params: &mut Vec<Box<dyn ToSql + Send>>
+    params: &mut Vec<Box<dyn ToSql + Send>>,
 ) {
     complete_frequency(q.frequency, query, params);
     complete_name(q.name.clone(), query, params);
     complete_cost(q.cost.clone(), query, params);
     complete_user(q.user, query, params);
     complete_category(q.category, query, params);
-    complete_date("payments.date >=".to_string(), q.start_date.clone(), query, params);
-    complete_date("payments.date <=".to_string(), q.end_date.clone(), query, params);
+    complete_date(
+        "payments.date >=".to_string(),
+        q.start_date.clone(),
+        query,
+        params,
+    );
+    complete_date(
+        "payments.date <=".to_string(),
+        q.end_date.clone(),
+        query,
+        params,
+    );
 }
 
-fn complete_frequency(frequency: Option<Frequency>, query: &mut String, params: &mut Vec<Box<dyn ToSql + Send>>) {
+fn complete_frequency(
+    frequency: Option<Frequency>,
+    query: &mut String,
+    params: &mut Vec<Box<dyn ToSql + Send>>,
+) {
     query.push_str("AND payments.frequency = ?");
-    params.push(Box::new(frequency.unwrap_or(Frequency::Punctual).to_string()));
+    params.push(Box::new(
+        frequency.unwrap_or(Frequency::Punctual).to_string(),
+    ));
 }
 
-fn complete_name(name: Option<String>, query: &mut String, params: &mut Vec<Box<dyn ToSql + Send>>) {
+fn complete_name(
+    name: Option<String>,
+    query: &mut String,
+    params: &mut Vec<Box<dyn ToSql + Send>>,
+) {
     if let Some(name) = name {
         if !name.is_empty() {
-            query.push_str(format!(
-                "AND {} LIKE ?",
-                utils::format_key_for_search("payments.name")
-            ).as_str());
+            query.push_str(
+                format!(
+                    "AND {} LIKE ?",
+                    utils::format_key_for_search("payments.name")
+                )
+                .as_str(),
+            );
             params.push(Box::new(name));
         }
     }
 }
 
-fn complete_cost(cost: Option<String>, query: &mut String, params: &mut Vec<Box<dyn ToSql + Send>>) {
+fn complete_cost(
+    cost: Option<String>,
+    query: &mut String,
+    params: &mut Vec<Box<dyn ToSql + Send>>,
+) {
     if let Some(cost) = cost {
         if !cost.is_empty() {
             query.push_str("AND payments.cost = ?");
@@ -178,14 +210,22 @@ fn complete_cost(cost: Option<String>, query: &mut String, params: &mut Vec<Box<
     }
 }
 
-fn complete_user(user: Option<i64>, query: &mut String, params: &mut Vec<Box<dyn ToSql + Send>>) {
+fn complete_user(
+    user: Option<i64>,
+    query: &mut String,
+    params: &mut Vec<Box<dyn ToSql + Send>>,
+) {
     if let Some(user) = user {
         query.push_str("AND payments.user_id = ?");
         params.push(Box::new(user))
     }
 }
 
-fn complete_category(category: Option<i64>, query: &mut String, params: &mut Vec<Box<dyn ToSql + Send>>) {
+fn complete_category(
+    category: Option<i64>,
+    query: &mut String,
+    params: &mut Vec<Box<dyn ToSql + Send>>,
+) {
     if let Some(category) = category {
         query.push_str("AND payments.category_id = ?");
         params.push(Box::new(category));
@@ -196,7 +236,7 @@ fn complete_date(
     name_and_op: String,
     date: Option<String>,
     query: &mut String,
-    params: &mut Vec<Box<dyn ToSql + Send>>
+    params: &mut Vec<Box<dyn ToSql + Send>>,
 ) {
     if let Some(date) = date {
         if !date.is_empty() {
@@ -224,7 +264,7 @@ pub async fn list_for_stats(conn: &Connection) -> Vec<payment::Stat> {
 
     let res = conn
         .call(move |conn| {
-            let mut stmt = conn.prepare(&query)?;
+            let mut stmt = conn.prepare(query)?;
             let payments = stmt
                 .query_map([], row_to_stat)?
                 .collect::<Result<Vec<payment::Stat>, _>>()?;
@@ -261,7 +301,7 @@ pub async fn get_row(conn: &Connection, id: i64, frequency: Frequency) -> i64 {
             let mut stmt = conn.prepare(query)?;
             let mut iter = stmt.query_map(
                 named_params![":id": id, ":frequency": frequency.to_string()],
-                |row| row.get(0)
+                |row| row.get(0),
             )?;
             utils::one::<i64, _>(&mut iter)
         })
@@ -295,7 +335,8 @@ pub async fn get_for_form(conn: &Connection, id: i64) -> Option<payment::Form> {
     let res = conn
         .call(move |conn| {
             let mut stmt = conn.prepare(query)?;
-            let mut iter = stmt.query_map(named_params![":id": id], row_to_form)?;
+            let mut iter =
+                stmt.query_map(named_params![":id": id], row_to_form)?;
             utils::one(&mut iter)
         })
         .await;
@@ -370,7 +411,6 @@ pub async fn update(conn: &Connection, id: i64, p: payment::Update) -> bool {
         })
         .await;
 
-
     match res {
         Ok(_) => true,
         Err(err) => {
@@ -400,14 +440,17 @@ pub async fn search_category(
     conn: &Connection,
     payment_name: String,
 ) -> Option<i64> {
-    let query = format!(r#"
+    let query = format!(
+        r#"
         SELECT category_id
         FROM payments
         WHERE
             deleted_at IS NULL
             AND {} LIKE :name
         ORDER BY updated_at, created_at
-    "#, utils::format_key_for_search("name"));
+    "#,
+        utils::format_key_for_search("name")
+    );
 
     let payment_name_closure = payment_name.clone();
 
@@ -424,7 +467,9 @@ pub async fn search_category(
 
     match res {
         Ok(category) => Some(category),
-        Err(tokio_rusqlite::Error::Rusqlite(rusqlite::Error::QueryReturnedNoRows)) => None,
+        Err(tokio_rusqlite::Error::Rusqlite(
+            rusqlite::Error::QueryReturnedNoRows,
+        )) => None,
         Err(err) => {
             log::error!(
                 "Error looking for the category of {}: {:?}",
@@ -448,7 +493,7 @@ pub async fn is_category_used(conn: &Connection, category_id: i64) -> bool {
 
     let res = conn
         .call(move |conn| {
-            let mut stmt = conn.prepare(&query)?;
+            let mut stmt = conn.prepare(query)?;
             Ok(stmt.exists(named_params![":category_id": category_id])?)
         })
         .await;
@@ -520,9 +565,7 @@ pub async fn create_monthly_payments(conn: &Connection) {
             AND deleted_at IS NULL
     "#;
 
-    let res = conn
-        .call(move |conn| Ok(conn.execute(query, [])?))
-        .await;
+    let res = conn.call(move |conn| Ok(conn.execute(query, [])?)).await;
 
     match res {
         Ok(_) => (),
diff --git a/src/model/frequency.rs b/src/model/frequency.rs
index 91aab89..f96482c 100644
--- a/src/model/frequency.rs
+++ b/src/model/frequency.rs
@@ -1,7 +1,9 @@
 use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ValueRef};
 use std::{fmt, str};
 
-#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq)]
+#[derive(
+    Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq,
+)]
 pub enum Frequency {
     Punctual,
     Monthly,
-- 
cgit v1.2.3