Skip to content

Commit

Permalink
habits done i think
Browse files Browse the repository at this point in the history
  • Loading branch information
Piyuuussshhh committed Sep 18, 2024
1 parent a9726bb commit 2b864f5
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 17 deletions.
129 changes: 116 additions & 13 deletions src-tauri/src/db/habits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,41 @@ pub struct Habit {
name: String,
streak: u64,
highest_streak: u64,
day_types: Vec<DayType>,
}

#[derive(Serialize, Deserialize, Debug)]
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct DayType {
id: u64,
habit_id: u64,
name: String,
color: String,
}

#[derive(Serialize, Deserialize, Debug)]
pub enum ToDelete {
All,
One(u64),
}

pub mod commands {
use std::collections::HashMap;

use crate::db::{habits::Habit, init::DbConn};
use rusqlite::{params, Connection};
use tauri::State;

use super::{DayType, ToDelete};

/// (C)RUD -> Create a habit.
#[tauri::command(rename_all = "snake_case")]
pub fn add_habit(
db_conn: State<'_, DbConn>,
name: String,
day_types: Vec<(String, String)>,
) -> (i64, HashMap<String, i64>) {
) -> (i64, HashMap<String, (i64, String)>) {
let conn = db_conn.lock().unwrap();

let command =
format!("INSERT INTO habits (name, streak, highest_streak) VALUES (?1, ?2, ?3)");
let mut stmt = conn
Expand All @@ -44,15 +55,16 @@ pub mod commands {
let day_types_map = day_types
.into_iter()
.map(|(dt_name, color)| {
let dt_id = insert_day_type(&conn, id, dt_name.as_str(), color);
let dt_id = insert_day_type(&conn, id, dt_name.as_str(), &color);

(dt_name, dt_id)
(dt_name, (dt_id, color))
})
.collect::<HashMap<String, i64>>();
.collect::<HashMap<String, (i64, String)>>();

(id, day_types_map)
}

/// (C)RUD -> Create a day type for a habit.
#[tauri::command(rename_all = "snake_case")]
pub fn add_day_type(
db_conn: State<'_, DbConn>,
Expand All @@ -62,9 +74,10 @@ pub mod commands {
) -> i64 {
let conn = db_conn.lock().unwrap();

insert_day_type(&conn, habit_id, &dt_name, color)
insert_day_type(&conn, habit_id, &dt_name, &color)
}

/// C(R)UD -> Fetch all habits.
#[tauri::command(rename_all = "snake_case")]
pub fn fetch_habits(db_conn: State<'_, DbConn>) -> String {
let conn = db_conn.lock().unwrap();
Expand All @@ -78,17 +91,20 @@ pub mod commands {
let streak: u64 = row.get(2)?;
let highest_streak: u64 = row.get(3)?;

let day_types = fetch_day_types(&conn, id);

Ok(Habit {
id,
name,
streak,
highest_streak,
day_types
})
}) {
Err(e) => panic!("[ERROR] Could not fetch habits: {e}"),
Ok(iterator) => iterator
.filter(|habit_op| habit_op.is_ok())
.map(|habit_op| match habit_op {
.filter(|habit_res| habit_res.is_ok())
.map(|habit_res| match habit_res {
Ok(habit) => habit,
Err(e) => {
panic!("[ERROR] Wtf why is Err() still here? I specifically didn't ask for it: {e}")
Expand All @@ -101,24 +117,62 @@ pub mod commands {
.expect("[ERROR] Could not convert habit vector into a JSON object")
}

// Note: This will update the highest_streak value if the streak exceeds it, but
// the frontend will have no way of knowing that. Therefore, it needs to be handled
// in the frontend separately.
/// CR(U)D -> Increment the streak for a habit.
#[tauri::command(rename_all = "snake_case")]
pub fn increment_streak(db_conn: State<'_, DbConn>, habit_id: u64, dt_id: u64) {
let conn = db_conn.lock().unwrap();

// Update streak.
match conn.execute(
"UPDATE habits SET streak = streak + 1 WHERE id=(?1)",
params![habit_id],
) {
Ok(_) => (),
Err(e) => panic!("[ERROR] Could not increment streak: {e}"),
};
match conn.execute(
"UPDATE habits SET highest_streak = streak WHERE highest_streak < streak",
[],
) {
Ok(_) => (),
Err(e) => panic!("[ERROR] Could not increment streak: {e}"),
};

// Add record in history.
let command = format!("INSERT INTO history VALUES (?1, ?2)");
let mut stmt = conn
.prepare(&command)
.expect("[ERROR] Could not prepare insertion into history command.");
stmt.insert(params![habit_id, dt_id])
.expect("[ERROR] Could not insert into history!");
}

/// CRU(D) -> Delete a single or all day types of a habit.
#[tauri::command(rename_all = "snake_case")]
pub fn delete_habit(db_conn: State<'_, DbConn>, habit_id: u64) {}
pub fn delete_day_type(db_conn: State<'_, DbConn>, habit_id: u64, how_many: ToDelete) {
let conn = db_conn.lock().unwrap();

#[tauri::command(rename_all = "snake_case")]
pub fn delete_day_types(db_conn: State<'_, DbConn>, habits_id: u64) {}
delete_dt_helper(&conn, habit_id, how_many);
}

/// CRU(D) -> Delete a habit.
#[tauri::command(rename_all = "snake_case")]
pub fn delete_history(db_conn: State<'_, DbConn>, habit_id: u64) {}
pub fn delete_habit(db_conn: State<'_, DbConn>, habit_id: u64) {
let conn = db_conn.lock().unwrap();

delete_history(&conn, habit_id);
delete_dt_helper(&conn, habit_id, ToDelete::All);

conn.execute("DELETE FROM habits WHERE id=(?1)", params![habit_id])
.expect("[ERROR] Could not delete habit!");
}

/* ------------------------------------ Helper Functions ------------------------------------ */

fn insert_day_type(conn: &Connection, habit_id: i64, dt_name: &str, color: String) -> i64 {
fn insert_day_type(conn: &Connection, habit_id: i64, dt_name: &str, color: &str) -> i64 {
let command = format!("INSERT INTO day_types (habit_id, name, color) VALUES (?1, ?2, ?3)");
let mut stmt = conn
.prepare(&command)
Expand All @@ -127,4 +181,53 @@ pub mod commands {
stmt.insert(params![habit_id, dt_name, color])
.expect("[ERROR] Could not insert day type!")
}

fn fetch_day_types(conn: &Connection, habit_id: u64) -> Vec<DayType> {
let command = format!("SELECT id, name, color FROM day_types WHERE habit_id = {habit_id}");
let mut stmt = conn
.prepare(&command)
.expect("[ERROR] Cannot prepare statement that retrieves a habit's day types!");

let day_types = match stmt.query_map([], |row| {
let dt_id = row.get(0)?;
let dt_name = row.get(1)?;
let dt_color = row.get(2)?;

Ok(DayType {
id: dt_id,
habit_id,
name: dt_name,
color: dt_color,
})
}) {
Err(e) => panic!("[ERROR] Could not fetch day types for habit {habit_id}: {e}"),
Ok(dt_iter) => dt_iter.filter(|dt_res| dt_res.is_ok()).map(|dt_res| match dt_res {
Ok(dt) => dt,
Err(e) => panic!("[ERROR] Wtf why is Err() still here in DT? I specifically didn't ask for it: {e}"),
}).collect::<Vec<DayType>>(),
};

day_types
}

fn delete_history(conn: &Connection, habit_id: u64) -> u64 {
conn.execute("DELETE FROM history WHERE habit_id=(?1)", params![habit_id])
.expect("[ERROR] Could not erase history of the habit!") as u64
}

fn delete_dt_helper(conn: &Connection, habit_id: u64, how_many: ToDelete) {
match how_many {
ToDelete::All => {
conn.execute(
"DELETE FROM day_types WHERE habit_id=(?1)",
params![habit_id],
)
.expect("[ERROR] Could not delete day types of the habit!");
}
ToDelete::One(dt_id) => {
conn.execute("DELETE FROM day_types WHERE id=(?1)", params![dt_id])
.expect("[ERROR] Could not delete one day type!");
}
}
}
}
8 changes: 4 additions & 4 deletions src-tauri/src/db/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ pub mod habits {
[],
)?;

// DAY TYPES
// HISTORY
conn.execute(
"CREATE TABLE IF NOT EXISTS history (
id INTEGER PRIMARY KEY AUTOINCREMENT,
Expand All @@ -195,7 +195,7 @@ pub mod habits {
pub fn validate_streaks(conn: &Connection) -> SQLiteResult<()> {
// Obtain all habit_ids.
let mut stmt = conn.prepare("SELECT id FROM habits")?;
let habits = stmt
let all_habits = stmt
.query_map([], |row| {
let id: u64 = row.get(0)?;
Ok(id)
Expand All @@ -204,7 +204,7 @@ pub mod habits {
.filter(|id| *id != u64::MAX)
.collect::<Vec<u64>>();

if habits.is_empty() {
if all_habits.is_empty() {
return Ok(());
}

Expand All @@ -228,7 +228,7 @@ pub mod habits {
.collect::<HashSet<u64>>();

// Check if all habit_ids are in the set.
habits.into_iter().for_each(|id| {
all_habits.into_iter().for_each(|id| {
// If a habit_id is not: set the corresponding habit's streak to 0.
if !yesterdays_habits.contains(&id) {
match conn.execute("UPDATE habits SET streak=0 WHERE id=(?id)", params![id]) {
Expand Down

0 comments on commit 2b864f5

Please sign in to comment.