feat: add first version of TougliGui with same features as on google sheet
This commit is contained in:
163
src-tauri/src/db.rs
Normal file
163
src-tauri/src/db.rs
Normal file
@ -0,0 +1,163 @@
|
||||
use rusqlite::{Connection, Result, params};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use chrono::Utc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct Profile {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct GuideRow {
|
||||
pub gid: String,
|
||||
pub name: String,
|
||||
pub data: String,
|
||||
pub last_synced_at: Option<String>,
|
||||
}
|
||||
|
||||
pub fn get_db_path() -> String {
|
||||
let data_dir = dirs_next::data_dir()
|
||||
.unwrap_or_else(|| std::path::PathBuf::from("."))
|
||||
.join("toughligui");
|
||||
std::fs::create_dir_all(&data_dir).ok();
|
||||
data_dir.join("toughligui.db").to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
pub fn open() -> Result<Connection> {
|
||||
let path = get_db_path();
|
||||
let conn = Connection::open(&path)?;
|
||||
conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON;")?;
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
pub fn migrate(conn: &Connection) -> Result<()> {
|
||||
conn.execute_batch("
|
||||
CREATE TABLE IF NOT EXISTS profiles (
|
||||
id TEXT PRIMARY KEY,
|
||||
name TEXT UNIQUE NOT NULL,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS guides (
|
||||
gid TEXT PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
data TEXT NOT NULL,
|
||||
last_synced_at TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS quest_completions (
|
||||
profile_id TEXT NOT NULL,
|
||||
quest_name TEXT NOT NULL,
|
||||
completed_at TEXT NOT NULL,
|
||||
PRIMARY KEY (profile_id, quest_name),
|
||||
FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_profiles(conn: &Connection) -> Result<Vec<Profile>> {
|
||||
let mut stmt = conn.prepare("SELECT id, name, created_at FROM profiles ORDER BY created_at ASC")?;
|
||||
let rows = stmt.query_map([], |row| {
|
||||
Ok(Profile {
|
||||
id: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
created_at: row.get(2)?,
|
||||
})
|
||||
})?;
|
||||
rows.collect()
|
||||
}
|
||||
|
||||
pub fn create_profile(conn: &Connection, name: &str) -> Result<Profile> {
|
||||
let id = Uuid::new_v4().to_string();
|
||||
let created_at = Utc::now().to_rfc3339();
|
||||
conn.execute(
|
||||
"INSERT INTO profiles (id, name, created_at) VALUES (?1, ?2, ?3)",
|
||||
params![id, name, created_at],
|
||||
)?;
|
||||
Ok(Profile { id, name: name.to_string(), created_at })
|
||||
}
|
||||
|
||||
pub fn delete_profile(conn: &Connection, profile_id: &str) -> Result<()> {
|
||||
conn.execute("DELETE FROM profiles WHERE id = ?1", params![profile_id])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_completed_quests(conn: &Connection, profile_id: &str) -> Result<Vec<String>> {
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT quest_name FROM quest_completions WHERE profile_id = ?1"
|
||||
)?;
|
||||
let rows = stmt.query_map(params![profile_id], |row| row.get(0))?;
|
||||
rows.collect()
|
||||
}
|
||||
|
||||
pub fn toggle_quest(conn: &Connection, profile_id: &str, quest_name: &str) -> Result<bool> {
|
||||
let exists: bool = conn.query_row(
|
||||
"SELECT COUNT(*) FROM quest_completions WHERE profile_id = ?1 AND quest_name = ?2",
|
||||
params![profile_id, quest_name],
|
||||
|row| row.get::<_, i64>(0),
|
||||
).map(|c| c > 0)?;
|
||||
|
||||
if exists {
|
||||
conn.execute(
|
||||
"DELETE FROM quest_completions WHERE profile_id = ?1 AND quest_name = ?2",
|
||||
params![profile_id, quest_name],
|
||||
)?;
|
||||
Ok(false)
|
||||
} else {
|
||||
let now = Utc::now().to_rfc3339();
|
||||
conn.execute(
|
||||
"INSERT INTO quest_completions (profile_id, quest_name, completed_at) VALUES (?1, ?2, ?3)",
|
||||
params![profile_id, quest_name, now],
|
||||
)?;
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upsert_guide(conn: &Connection, gid: &str, name: &str, data: &str) -> Result<()> {
|
||||
let now = Utc::now().to_rfc3339();
|
||||
conn.execute(
|
||||
"INSERT INTO guides (gid, name, data, last_synced_at) VALUES (?1, ?2, ?3, ?4)
|
||||
ON CONFLICT(gid) DO UPDATE SET name=excluded.name, data=excluded.data, last_synced_at=excluded.last_synced_at",
|
||||
params![gid, name, data, now],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_guides(conn: &Connection) -> Result<Vec<GuideRow>> {
|
||||
let mut stmt = conn.prepare("SELECT gid, name, data, last_synced_at FROM guides ORDER BY rowid ASC")?;
|
||||
let rows = stmt.query_map([], |row| {
|
||||
Ok(GuideRow {
|
||||
gid: row.get(0)?,
|
||||
name: row.get(1)?,
|
||||
data: row.get(2)?,
|
||||
last_synced_at: row.get(3)?,
|
||||
})
|
||||
})?;
|
||||
rows.collect()
|
||||
}
|
||||
|
||||
pub fn get_setting(conn: &Connection, key: &str) -> Option<String> {
|
||||
conn.query_row(
|
||||
"SELECT value FROM settings WHERE key = ?1",
|
||||
params![key],
|
||||
|row| row.get(0),
|
||||
).ok()
|
||||
}
|
||||
|
||||
pub fn set_setting(conn: &Connection, key: &str, value: &str) -> Result<()> {
|
||||
conn.execute(
|
||||
"INSERT INTO settings (key, value) VALUES (?1, ?2)
|
||||
ON CONFLICT(key) DO UPDATE SET value=excluded.value",
|
||||
params![key, value],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
Reference in New Issue
Block a user