2022-09-14 17:51:12 +00:00
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
use futures_util::TryStreamExt;
|
|
|
|
use sqlx::{
|
|
|
|
sqlite::{SqliteConnectOptions, SqliteQueryResult, SqliteRow},
|
|
|
|
ConnectOptions, Executor, Row, SqlitePool,
|
|
|
|
};
|
|
|
|
use tracing::{debug, info, log::LevelFilter};
|
|
|
|
|
|
|
|
use super::{
|
|
|
|
error::{Error, Result},
|
|
|
|
User,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// SQLite database
|
2022-09-20 18:02:45 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2022-09-14 17:51:12 +00:00
|
|
|
pub struct Database {
|
|
|
|
/// Sqlite connection pool
|
|
|
|
pub pool: SqlitePool,
|
|
|
|
}
|
|
|
|
|
2022-09-21 18:53:22 +00:00
|
|
|
pub const DATABASE_TABLES: &str = include_str!("../../sql/tables.sql");
|
|
|
|
|
2022-09-14 17:51:12 +00:00
|
|
|
impl Database {
|
|
|
|
/// Open a SQLite database
|
|
|
|
pub async fn open(path: &str) -> Result<Self> {
|
|
|
|
debug!("Opening SQLite database: {}", path);
|
|
|
|
|
|
|
|
// sqlite connection options
|
2022-09-18 13:25:42 +00:00
|
|
|
let mut options =
|
|
|
|
SqliteConnectOptions::from_str(path).map_err(|e| Error::OpenDatabase(e.to_string()))?;
|
2022-09-14 17:51:12 +00:00
|
|
|
|
|
|
|
// set log level to Debug
|
|
|
|
options.log_statements(LevelFilter::Debug);
|
|
|
|
|
|
|
|
// create a database pool
|
|
|
|
let pool = SqlitePool::connect_with(options)
|
|
|
|
.await
|
2022-09-18 13:25:42 +00:00
|
|
|
.map_err(|e| Error::ConnectDatabase(e.to_string()))?;
|
2022-09-14 17:51:12 +00:00
|
|
|
|
|
|
|
info!("Connected to database!");
|
|
|
|
|
|
|
|
Ok(Self { pool })
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Create all required tables for HomeDisk.
|
|
|
|
pub async fn create_tables(&self) -> Result<SqliteQueryResult> {
|
|
|
|
self.pool
|
2022-09-21 18:53:22 +00:00
|
|
|
.execute(DATABASE_TABLES)
|
2022-09-14 17:51:12 +00:00
|
|
|
.await
|
2022-09-18 13:25:42 +00:00
|
|
|
.map_err(|e| Error::CreateTables(e.to_string()))
|
2022-09-14 17:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Create new user in the database.
|
|
|
|
pub async fn create_user(&self, user: &User) -> Result<SqliteQueryResult> {
|
|
|
|
debug!("Creating user - {}", user.username);
|
|
|
|
|
|
|
|
// build sql query
|
|
|
|
let query = sqlx::query("INSERT INTO user (id, username, password) VALUES (?, ?, ?)")
|
|
|
|
.bind(&user.id)
|
|
|
|
.bind(&user.username)
|
|
|
|
.bind(&user.password);
|
|
|
|
|
2022-09-18 13:25:42 +00:00
|
|
|
self.pool
|
|
|
|
.execute(query)
|
|
|
|
.await
|
|
|
|
.map_err(|e| Error::Execute(e.to_string()))
|
2022-09-14 17:51:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Search for a user.
|
|
|
|
pub async fn find_user(&self, user: &User) -> Result<User> {
|
|
|
|
debug!("Searching for a user - {}", user.username);
|
|
|
|
|
|
|
|
// create query request to database
|
|
|
|
let query = sqlx::query("SELECT * FROM user WHERE username = ? AND password = ?")
|
|
|
|
.bind(&user.username)
|
|
|
|
.bind(&user.password);
|
|
|
|
|
|
|
|
// fetch query
|
|
|
|
let mut stream = self.pool.fetch(query);
|
|
|
|
|
|
|
|
// get rows from query
|
|
|
|
let row = stream
|
|
|
|
.try_next()
|
|
|
|
.await
|
2022-09-18 13:25:42 +00:00
|
|
|
.map_err(|e| Error::Execute(e.to_string()))?
|
2022-09-14 17:51:12 +00:00
|
|
|
.ok_or(Error::UserNotFound)?;
|
|
|
|
|
|
|
|
Self::find(row)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Search for a user by UUID.
|
|
|
|
pub async fn find_user_by_id(&self, id: &str) -> Result<User> {
|
|
|
|
debug!("Searching for a user by UUID - {}", id);
|
|
|
|
|
|
|
|
// create query request to database
|
|
|
|
let query = sqlx::query("SELECT * FROM user WHERE id = ?").bind(id);
|
|
|
|
|
|
|
|
// fetch query
|
|
|
|
let mut stream = self.pool.fetch(query);
|
|
|
|
|
|
|
|
// get rows from query
|
|
|
|
let row = stream
|
|
|
|
.try_next()
|
|
|
|
.await
|
2022-09-18 13:25:42 +00:00
|
|
|
.map_err(|e| Error::Execute(e.to_string()))?
|
2022-09-14 17:51:12 +00:00
|
|
|
.ok_or(Error::UserNotFound)?;
|
|
|
|
|
|
|
|
Self::find(row)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn find(row: SqliteRow) -> Result<User> {
|
|
|
|
// get `id` row
|
2022-09-18 13:25:42 +00:00
|
|
|
let id = row
|
|
|
|
.try_get("id")
|
|
|
|
.map_err(|e| Error::GetRow(e.to_string()))?;
|
2022-09-14 17:51:12 +00:00
|
|
|
// get `username` row
|
2022-09-18 13:25:42 +00:00
|
|
|
let username = row
|
|
|
|
.try_get("username")
|
|
|
|
.map_err(|e| Error::GetRow(e.to_string()))?;
|
2022-09-14 17:51:12 +00:00
|
|
|
// get `password` row
|
2022-09-18 13:25:42 +00:00
|
|
|
let password = row
|
|
|
|
.try_get("password")
|
|
|
|
.map_err(|e| Error::GetRow(e.to_string()))?;
|
2022-09-14 17:51:12 +00:00
|
|
|
|
|
|
|
Ok(User {
|
|
|
|
id,
|
|
|
|
username,
|
|
|
|
password,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
const USERNAME: &str = "medzik";
|
|
|
|
const PASSWORD: &str = "SuperSecretPassword123!";
|
|
|
|
|
|
|
|
async fn open_db() -> Database {
|
|
|
|
Database::open("sqlite::memory:")
|
|
|
|
.await
|
|
|
|
.expect("Failed to open database in memory")
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn new_user() -> Database {
|
|
|
|
let db = open_db().await;
|
|
|
|
|
|
|
|
// create tables
|
|
|
|
db.create_tables().await.expect("create tables");
|
|
|
|
|
|
|
|
// create new user
|
|
|
|
let user = User::new(USERNAME, PASSWORD, true);
|
|
|
|
db.create_user(&user).await.expect("create user");
|
|
|
|
|
|
|
|
db
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_create_user() {
|
|
|
|
new_user().await;
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_find_user() {
|
|
|
|
let db = new_user().await;
|
|
|
|
|
2022-09-18 13:25:42 +00:00
|
|
|
let user = db
|
|
|
|
.find_user(&User::new(USERNAME, PASSWORD, false))
|
|
|
|
.await
|
|
|
|
.unwrap();
|
2022-09-14 17:51:12 +00:00
|
|
|
|
|
|
|
assert_eq!(user.username, USERNAME)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn test_find_user_wrong_password() {
|
|
|
|
let db = new_user().await;
|
|
|
|
|
|
|
|
let err = db
|
|
|
|
.find_user(&User::new(USERNAME, "wrong password 123!", false))
|
|
|
|
.await
|
|
|
|
.unwrap_err();
|
|
|
|
|
2022-09-18 13:25:42 +00:00
|
|
|
assert_eq!(err, Error::UserNotFound)
|
2022-09-14 17:51:12 +00:00
|
|
|
}
|
|
|
|
}
|