summaryrefslogtreecommitdiff
path: root/src/database/database.rs
diff options
context:
space:
mode:
authorDaniel Hader <[email protected]>2026-05-11 19:28:00 -0500
committerDaniel Hader <[email protected]>2026-05-11 19:28:00 -0500
commit36fad793c3be58b220ae319a45c8cd8afbae09fa (patch)
tree0f072c9f3dd45b4295ded00d8a7f2c318238b688 /src/database/database.rs
parenta005add5513182abb0b1230cf514c7a45b290e4b (diff)
user route and database pooling using r2d2
Diffstat (limited to 'src/database/database.rs')
-rw-r--r--src/database/database.rs158
1 files changed, 123 insertions, 35 deletions
diff --git a/src/database/database.rs b/src/database/database.rs
index 7da66f2..ca0e018 100644
--- a/src/database/database.rs
+++ b/src/database/database.rs
@@ -1,67 +1,154 @@
use std::path::Path;
-use rusqlite::{Connection, Error};
-use crate::database::problem::Problem;
+use r2d2::Pool;
+use r2d2_sqlite::SqliteConnectionManager;
+use r2d2_sqlite::rusqlite::OptionalExtension;
+use super::problem::Problem;
+use super::user::User;
+
+#[derive(Clone)]
pub struct Database {
- connection: Connection,
+ pool: Pool<SqliteConnectionManager>
+}
+
+#[derive(Debug)]
+pub enum DatabaseError {
+ Connection(String),
+ Query(String),
}
impl Database {
- pub fn new(database_path: impl AsRef<Path>) -> Result<Self, Error> {
- let connection = Connection::open(database_path)?;
- Ok(Database {
- connection,
- })
+ pub fn new(database_path: impl AsRef<Path>) -> Result<Self, DatabaseError> {
+ let manager = SqliteConnectionManager::file(database_path);
+ let pool = Pool::new(manager)
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ Ok(Database { pool })
+ }
+
+ pub fn new_in_memory() -> Result<Self, DatabaseError> {
+ let manager = SqliteConnectionManager::memory();
+ let pool = Pool::new(manager)
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ Ok(Database { pool })
+ }
+
+ pub fn insert_user(&self, email: &str, username: &str, password_hash: &str) -> Result<User, DatabaseError> {
+ static QUERY: &str = include_str!("sql/insert_user.sql");
+ let conn = self.pool
+ .get()
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ let mut statement = conn.prepare(QUERY)
+ .map_err(|e| DatabaseError::Query(e.to_string()))?;
+
+ Ok(statement
+ .query_one((email, username, password_hash), |row| {
+ Ok(User::new(
+ row.get("id")?,
+ email.to_owned(),
+ username.to_owned(),
+ password_hash.to_owned(),
+ ))
+ })
+ .map_err(|e| DatabaseError::Query(e.to_string()))?
+ )
+ }
+
+ pub fn fetch_user_by_email(&self, email: &str) -> Result<Option<User>, DatabaseError> {
+ static QUERY: &str = include_str!("sql/fetch_user_by_email.sql");
+ let conn = self.pool
+ .get()
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ let mut statement = conn.prepare(QUERY)
+ .map_err(|e| DatabaseError::Query(e.to_string()))?;
+
+ Ok(statement
+ .query_one([email], |row| {
+ Ok(User::new(
+ row.get("id")?,
+ row.get("email")?,
+ row.get("username")?,
+ row.get("password_hash")?,
+ ))
+ })
+ .optional()
+ .map_err(|e| DatabaseError::Query(e.to_string()))?
+ )
}
- pub fn new_in_memory() -> Result<Self, Error> {
- let connection = Connection::open_in_memory()?;
- Ok(Database {
- connection,
- })
+ pub fn fetch_user_by_username(&self, username: &str) -> Result<Option<User>, DatabaseError> {
+ static QUERY: &str = include_str!("sql/fetch_user_by_username.sql");
+ let conn = self.pool
+ .get()
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ let mut statement = conn.prepare(QUERY)
+ .map_err(|e| DatabaseError::Query(e.to_string()))?;
+
+ Ok(statement
+ .query_one([username], |row| {
+ Ok(User::new(
+ row.get("id")?,
+ row.get("email")?,
+ row.get("username")?,
+ row.get("password_hash")?,
+ ))
+ })
+ .optional()
+ .map_err(|e| DatabaseError::Query(e.to_string()))?
+ )
}
- pub fn insert_problem(&self, title: &str, description: &str) -> Result<Problem, Error> {
+ pub fn insert_problem(&self, title: &str, description: &str) -> Result<Problem, DatabaseError> {
static QUERY: &str = include_str!("sql/insert_problem.sql");
- let mut statement = self.connection.prepare(QUERY)?;
+ let conn = self.pool
+ .get()
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ let mut statement = conn.prepare(QUERY)
+ .map_err(|e| DatabaseError::Query(e.to_string()))?;
- let result = statement
+ Ok(statement
.query_one((title, description), |row| {
Ok(Problem::new(
row.get("id")?,
title.to_owned(),
description.to_owned(),
))
- })?;
-
- Ok(result)
- }
-
- pub fn delete_problem(&self, id: i64) -> Result<(), Error> {
- todo!();
+ })
+ .map_err(|e| DatabaseError::Query(e.to_string()))?
+ )
}
- pub fn fetch_problems(&self) -> Result<Vec<Problem>, Error> {
+ pub fn fetch_problems(&self) -> Result<Vec<Problem>, DatabaseError> {
static QUERY: &str = include_str!("sql/fetch_problems.sql");
- let mut statement = self.connection.prepare(QUERY)?;
+ let conn = self.pool
+ .get()
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+ let mut statement = conn.prepare(QUERY)
+ .map_err(|e| DatabaseError::Query(e.to_string()))?;
- let problems = statement
+ Ok(statement
.query_map([], |row| {
Ok(Problem::new(
row.get("id")?,
row.get("title")?,
row.get("description")?,
))
- })?
- .collect::<Result<Vec<Problem>, _>>()?;
-
- Ok(problems)
+ })
+ .map_err(|e| DatabaseError::Query(e.to_string()))?
+ .collect::<Result<Vec<Problem>, _>>()
+ .map_err(|e| DatabaseError::Query(e.to_string()))?
+ )
}
-
- pub fn initialize(&self) -> Result<(), Error> {
- static QUERY: &str = include_str!("sql/initialize.sql");
- self.connection.execute_batch(QUERY)?;
+
+ pub fn initialize(&self) -> Result<(), DatabaseError> {
+ static QUERY: &str = include_str!("sql/initialize.sql");
+ let conn = self.pool
+ .get()
+ .map_err(|e| DatabaseError::Connection(e.to_string()))?;
+
+ conn.execute_batch(QUERY)
+ .map_err(|e| DatabaseError::Query(e.to_string()))?;
+
Ok(())
}
}
@@ -82,10 +169,11 @@ mod tests {
let problem = db.insert_problem(title, description).unwrap();
assert_eq!(problem.title(), title);
assert_eq!(problem.description(), description);
-
+
let problems = db.fetch_problems().unwrap();
assert_eq!(problems.len(), 1);
assert_eq!(problems[0].title(), title);
assert_eq!(problems[0].description(), description);
+ assert_eq!(problems[0].id(), problem.id());
}
}