From 376e18d5ffc6d00ff2815bbc38e8c584433f833c Mon Sep 17 00:00:00 2001 From: Nettika Date: Sun, 25 Jan 2026 14:32:40 -0800 Subject: [PATCH] Create database interface --- src/db.rs | 96 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + todo.md | 2 +- 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 src/db.rs diff --git a/src/db.rs b/src/db.rs new file mode 100644 index 0000000..0be33c8 --- /dev/null +++ b/src/db.rs @@ -0,0 +1,96 @@ +use crate::project::Project; +use rusqlite::{Connection, Result}; + +pub struct Database { + conn: Connection, +} + +impl Database { + pub fn new(path: &str) -> Result { + let conn = Connection::open(path)?; + + conn.execute_batch(include_str!("schema.sql"))?; + + Ok(Self { conn }) + } + + pub fn create_project(&self, title: String) -> Result { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + + self.conn.execute( + "INSERT INTO projects (title, created_time, last_modified_time, percentage_completed, archived) + VALUES (?1, ?2, ?3, ?4, ?5)", + (&title, now, now, 0, 0), + )?; + + Ok(self.conn.last_insert_rowid()) + } + + pub fn update_project_progress(&self, id: i64, percentage: i32) -> Result<()> { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + + self.conn.execute( + "UPDATE projects SET percentage_completed = ?1, last_modified_time = ?2 WHERE id = ?3", + (percentage, now, id), + )?; + + Ok(()) + } + + pub fn archive_project(&self, id: i64) -> Result<()> { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + + self.conn.execute( + "UPDATE projects SET archived = 1, last_modified_time = ?1 WHERE id = ?2", + (now, id), + )?; + + Ok(()) + } + + pub fn unarchive_project(&self, id: i64) -> Result<()> { + let now = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_secs() as i64; + + self.conn.execute( + "UPDATE projects SET archived = 0, last_modified_time = ?1 WHERE id = ?2", + (now, id), + )?; + + Ok(()) + } + + pub fn list_all_projects(&self) -> Result> { + let mut stmt = self.conn.prepare( + "SELECT id, title, created_time, last_modified_time, percentage_completed, archived + FROM projects + ORDER BY last_modified_time DESC", + )?; + + let projects = stmt + .query_map([], |row| { + Ok(Project { + id: row.get(0)?, + title: row.get(1)?, + created_time: row.get(2)?, + last_modified_time: row.get(3)?, + percentage_completed: row.get(4)?, + archived: row.get::<_, i32>(5)? != 0, + }) + })? + .collect::>>()?; + + Ok(projects) + } +} diff --git a/src/main.rs b/src/main.rs index 96fb41a..7bdf1a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ mod project; +mod db; fn main() { println!("Starting server on localhost:8080"); diff --git a/todo.md b/todo.md index 5a67a2e..4dcd369 100644 --- a/todo.md +++ b/todo.md @@ -3,7 +3,7 @@ [x] Template a Rouille server that always returns an empty 404. [x] Create a database schema. There should just be one table for now: `projects`. The Projects table contains info about each project in the board. The table should have columns for title, created time, last modified time, percentage completed (integer), and a flag for archived. Since this is a single-user server, there doesn't need to be an metadata about whose project it is. [x] Create a `Project` struct that corresponds to the schema definition. -[ ] Create a module or struct at your discretion for interfacing with the database. Create functions for the following: create a project, update a project's progress, archive a project, unarchive a project, and list of all projects. +[x] Create a module or struct at your discretion for interfacing with the database. Create functions for the following: create a project, update a project's progress, archive a project, unarchive a project, and list of all projects. [ ] Create a `main.css` file in src. Use mvp.css as a starting point. [ ] Create a `project-card.js` file in src that creates a web component for displaying a project. [ ] Using a Rouille router, create `GET /main.css` and `GET /project-card.js` endpoints that returns the relevant files. Use the include_bytes! macro.