2022-04-21 18:54:53 +00:00
|
|
|
use futures_util::TryStreamExt;
|
2022-04-16 22:09:23 +00:00
|
|
|
use log::debug;
|
2022-04-21 18:54:53 +00:00
|
|
|
use sqlx::{sqlite::SqliteQueryResult, Executor, Row, SqlitePool};
|
2022-04-16 22:09:23 +00:00
|
|
|
|
2022-04-24 12:50:25 +00:00
|
|
|
use super::{Error, User};
|
2022-04-16 22:09:23 +00:00
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
/// SQL Database
|
2022-04-19 11:05:47 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2022-04-16 22:09:23 +00:00
|
|
|
pub struct Database {
|
2022-06-08 19:16:12 +00:00
|
|
|
/// SQLite Connection Pool
|
2022-04-19 11:05:47 +00:00
|
|
|
pub conn: SqlitePool,
|
2022-04-16 22:09:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Database {
|
2022-06-08 22:02:20 +00:00
|
|
|
/// Open a SQLite database
|
2022-06-18 11:39:15 +00:00
|
|
|
/// ```no_run
|
2022-04-23 18:44:02 +00:00
|
|
|
/// use homedisk_database::Database;
|
2022-04-17 22:07:37 +00:00
|
|
|
///
|
2022-06-14 20:37:42 +00:00
|
|
|
/// #[tokio::main]
|
2022-06-18 11:39:15 +00:00
|
|
|
/// async fn main() -> anyhow::Result<()> {
|
2022-06-14 20:37:42 +00:00
|
|
|
/// // open database in memory
|
2022-06-18 11:39:15 +00:00
|
|
|
/// Database::open("sqlite::memory:").await?;
|
2022-06-08 19:16:12 +00:00
|
|
|
///
|
2022-06-14 20:37:42 +00:00
|
|
|
/// // open database from file
|
2022-06-18 11:39:15 +00:00
|
|
|
/// Database::open("path/to/database.db").await?;
|
|
|
|
///
|
|
|
|
/// Ok(())
|
2022-06-14 20:37:42 +00:00
|
|
|
/// }
|
2022-04-17 22:07:37 +00:00
|
|
|
/// ```
|
2022-04-19 11:05:47 +00:00
|
|
|
pub async fn open(path: &str) -> Result<Self, Error> {
|
2022-06-07 20:36:26 +00:00
|
|
|
debug!("Opening SQLite database");
|
2022-04-16 22:09:23 +00:00
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
// create a database pool
|
2022-04-19 11:05:47 +00:00
|
|
|
let conn = SqlitePool::connect(path).await?;
|
2022-04-17 22:07:37 +00:00
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
// return `Database`
|
2022-04-16 22:09:23 +00:00
|
|
|
Ok(Self { conn })
|
|
|
|
}
|
|
|
|
|
2022-06-08 22:02:20 +00:00
|
|
|
/// Create a new User
|
2022-06-18 11:39:15 +00:00
|
|
|
/// ```no_run
|
2022-04-23 18:44:02 +00:00
|
|
|
/// use homedisk_database::{Database, User};
|
2022-04-17 22:07:37 +00:00
|
|
|
///
|
2022-06-14 20:37:42 +00:00
|
|
|
/// #[tokio::main]
|
2022-06-18 11:39:15 +00:00
|
|
|
/// async fn main() -> anyhow::Result<()> {
|
|
|
|
/// // open database
|
|
|
|
/// let db = Database::open("path/to/database.db").await?;
|
2022-06-14 20:37:42 +00:00
|
|
|
///
|
|
|
|
/// // create `User` type
|
|
|
|
/// let user = User::new("username", "password");
|
2022-06-08 19:16:12 +00:00
|
|
|
///
|
2022-06-14 20:37:42 +00:00
|
|
|
/// // create a user in database
|
2022-06-18 11:39:15 +00:00
|
|
|
/// db.create_user(&user).await?;
|
|
|
|
///
|
|
|
|
/// Ok(())
|
2022-06-14 20:37:42 +00:00
|
|
|
/// }
|
2022-04-17 22:07:37 +00:00
|
|
|
/// ```
|
2022-04-24 12:50:25 +00:00
|
|
|
pub async fn create_user(&self, user: &User) -> Result<SqliteQueryResult, Error> {
|
2022-06-07 20:36:26 +00:00
|
|
|
debug!("Creating user - {}", user.username);
|
2022-04-16 22:09:23 +00:00
|
|
|
|
2022-06-16 12:14:40 +00:00
|
|
|
// insert user to a database
|
2022-04-19 11:05:47 +00:00
|
|
|
let query = sqlx::query("INSERT INTO user (id, username, password) VALUES (?, ?, ?)")
|
2022-04-19 13:14:17 +00:00
|
|
|
.bind(&user.id)
|
|
|
|
.bind(&user.username)
|
|
|
|
.bind(&user.password);
|
2022-04-19 11:05:47 +00:00
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
// execute query and return output
|
2022-04-19 11:05:47 +00:00
|
|
|
Ok(self.conn.execute(query).await?)
|
2022-04-16 22:09:23 +00:00
|
|
|
}
|
2022-04-21 18:54:53 +00:00
|
|
|
|
2022-06-08 17:08:06 +00:00
|
|
|
/// Search for a user
|
2022-06-14 20:37:42 +00:00
|
|
|
/// ```
|
2022-05-01 16:47:26 +00:00
|
|
|
/// use homedisk_database::{Database, User};
|
|
|
|
///
|
2022-06-14 20:37:42 +00:00
|
|
|
/// #[tokio::main]
|
2022-06-18 11:39:15 +00:00
|
|
|
/// async fn find_user() -> anyhow::Result<()> {
|
|
|
|
/// // open database
|
|
|
|
/// let db = Database::open("path/to/database.db").await?;
|
2022-06-14 20:37:42 +00:00
|
|
|
///
|
|
|
|
/// // create `User` type
|
|
|
|
/// let user = User::new("username", "password");
|
|
|
|
///
|
|
|
|
/// // create a user in database
|
2022-06-18 11:39:15 +00:00
|
|
|
/// db.create_user(&user).await?;
|
2022-05-01 16:47:26 +00:00
|
|
|
///
|
2022-06-14 20:37:42 +00:00
|
|
|
/// // search for a user
|
2022-06-18 11:39:15 +00:00
|
|
|
/// db.find_user(&user).await?;
|
|
|
|
///
|
|
|
|
/// Ok(())
|
2022-06-14 20:37:42 +00:00
|
|
|
/// }
|
2022-05-01 16:47:26 +00:00
|
|
|
/// ```
|
2022-06-18 11:39:15 +00:00
|
|
|
pub async fn find_user(&self, user: &User) -> Result<User, Error> {
|
|
|
|
debug!("Searching for a user - {}", user.username);
|
2022-04-21 18:54:53 +00:00
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
// create query request to database
|
2022-04-21 18:54:53 +00:00
|
|
|
let query =
|
|
|
|
sqlx::query_as::<_, User>("SELECT * FROM user WHERE username = ? AND password = ?")
|
2022-06-18 11:39:15 +00:00
|
|
|
.bind(&user.username)
|
|
|
|
.bind(&user.password);
|
2022-04-21 18:54:53 +00:00
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
// fetch query
|
2022-04-21 18:54:53 +00:00
|
|
|
let mut stream = self.conn.fetch(query);
|
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
// get rows from query
|
2022-04-21 18:54:53 +00:00
|
|
|
let row = stream.try_next().await?.ok_or(Error::UserNotFound)?;
|
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
// get `id` row
|
2022-04-21 18:54:53 +00:00
|
|
|
let id = row.try_get("id")?;
|
2022-06-08 19:16:12 +00:00
|
|
|
// get `username` row
|
2022-04-21 18:54:53 +00:00
|
|
|
let username = row.try_get("username")?;
|
2022-06-08 19:16:12 +00:00
|
|
|
// get `password` row
|
2022-04-21 18:54:53 +00:00
|
|
|
let password = row.try_get("password")?;
|
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
// return `User`
|
2022-04-21 18:54:53 +00:00
|
|
|
Ok(User {
|
2022-04-23 21:48:20 +00:00
|
|
|
id,
|
|
|
|
username,
|
|
|
|
password,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2022-06-08 17:08:06 +00:00
|
|
|
/// Search for a user by UUID
|
2022-06-14 20:37:42 +00:00
|
|
|
/// ```
|
2022-05-01 16:47:26 +00:00
|
|
|
/// use homedisk_database::{Database, User};
|
|
|
|
///
|
2022-06-14 20:37:42 +00:00
|
|
|
/// #[tokio::main]
|
2022-06-18 11:39:15 +00:00
|
|
|
/// async fn find_user_by_id() -> anyhow::Result<()> {
|
|
|
|
/// // open database
|
|
|
|
/// let db = Database::open("path/to/database.db").await?;
|
2022-06-14 20:37:42 +00:00
|
|
|
///
|
|
|
|
/// // create `User` type
|
|
|
|
/// let user = User::new("username", "password");
|
|
|
|
///
|
|
|
|
/// // create a user in database
|
2022-06-18 11:39:15 +00:00
|
|
|
/// db.create_user(&user).await?;
|
2022-05-01 16:47:26 +00:00
|
|
|
///
|
2022-06-14 20:37:42 +00:00
|
|
|
/// // search for a user using UUID
|
2022-06-18 11:39:15 +00:00
|
|
|
/// db.find_user_by_id(&user.id).await?;
|
|
|
|
///
|
|
|
|
/// Ok(())
|
2022-06-14 20:37:42 +00:00
|
|
|
/// }
|
2022-05-01 16:47:26 +00:00
|
|
|
/// ```
|
2022-06-14 20:37:42 +00:00
|
|
|
pub async fn find_user_by_id(&self, id: &str) -> Result<User, Error> {
|
2022-06-07 20:36:26 +00:00
|
|
|
debug!("Searching for a user by UUID - {}", id);
|
2022-05-01 16:47:26 +00:00
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
// create query request to database
|
2022-04-23 21:48:20 +00:00
|
|
|
let query = sqlx::query_as::<_, User>("SELECT * FROM user WHERE id = ?").bind(id);
|
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
// fetch query
|
2022-04-23 21:48:20 +00:00
|
|
|
let mut stream = self.conn.fetch(query);
|
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
// get rows from query
|
2022-04-23 21:48:20 +00:00
|
|
|
let row = stream.try_next().await?.ok_or(Error::UserNotFound)?;
|
|
|
|
|
2022-06-08 19:16:12 +00:00
|
|
|
// get `id` row
|
2022-04-23 21:48:20 +00:00
|
|
|
let id = row.try_get("id")?;
|
2022-06-08 19:16:12 +00:00
|
|
|
// get `username` row
|
2022-04-23 21:48:20 +00:00
|
|
|
let username = row.try_get("username")?;
|
2022-06-08 19:16:12 +00:00
|
|
|
// get `password` row
|
2022-04-23 21:48:20 +00:00
|
|
|
let password = row.try_get("password")?;
|
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
// return `User`
|
2022-04-23 21:48:20 +00:00
|
|
|
Ok(User {
|
2022-04-21 18:54:53 +00:00
|
|
|
id,
|
|
|
|
username,
|
|
|
|
password,
|
|
|
|
})
|
|
|
|
}
|
2022-04-16 22:09:23 +00:00
|
|
|
}
|
2022-04-19 19:08:13 +00:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use std::fs;
|
|
|
|
|
|
|
|
use sqlx::Executor;
|
|
|
|
|
2022-04-23 18:44:02 +00:00
|
|
|
use crate::{Database, User};
|
2022-04-19 19:08:13 +00:00
|
|
|
|
2022-06-18 11:39:15 +00:00
|
|
|
const USERNAME: &str = "medzik";
|
|
|
|
const PASSWORD: &str = "SuperSecretPassword123!";
|
|
|
|
|
2022-06-11 19:36:29 +00:00
|
|
|
/// Utils to open database in tests
|
2022-04-19 19:08:13 +00:00
|
|
|
async fn open_db() -> Database {
|
|
|
|
Database::open("sqlite::memory:").await.expect("open db")
|
|
|
|
}
|
|
|
|
|
2022-06-08 17:08:06 +00:00
|
|
|
/// Utils to create a new user in tests
|
2022-04-21 18:54:53 +00:00
|
|
|
async fn new_user(db: &Database) {
|
|
|
|
// create user table
|
|
|
|
db.conn
|
|
|
|
.execute(sqlx::query(
|
|
|
|
&fs::read_to_string("../tables.sql").expect("open tables file"),
|
|
|
|
))
|
|
|
|
.await
|
|
|
|
.expect("create tables");
|
|
|
|
|
|
|
|
// create new user
|
2022-06-18 11:39:15 +00:00
|
|
|
let user = User::new(USERNAME, PASSWORD);
|
2022-04-21 18:54:53 +00:00
|
|
|
db.create_user(&user).await.expect("create user");
|
|
|
|
}
|
|
|
|
|
2022-06-18 11:39:15 +00:00
|
|
|
/// Test a create user
|
|
|
|
#[tokio::test]
|
|
|
|
async fn create_user() {
|
|
|
|
let db = open_db().await;
|
|
|
|
new_user(&db).await;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test a search for a user
|
|
|
|
#[tokio::test]
|
|
|
|
async fn find_user() {
|
|
|
|
let db = open_db().await;
|
|
|
|
|
|
|
|
new_user(&db).await;
|
|
|
|
|
|
|
|
let user = User::new(USERNAME, PASSWORD);
|
|
|
|
|
|
|
|
let user = db.find_user(&user).await.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(user.username, USERNAME)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test a search for a user by id
|
|
|
|
#[tokio::test]
|
|
|
|
async fn find_user_by_id() {
|
|
|
|
let db = open_db().await;
|
|
|
|
new_user(&db).await;
|
|
|
|
|
|
|
|
let user = User::new(USERNAME, PASSWORD);
|
|
|
|
|
|
|
|
let user = db.find_user_by_id(&user.id).await.unwrap();
|
|
|
|
|
|
|
|
assert_eq!(user.username, USERNAME)
|
|
|
|
}
|
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
/// Test a search for a user with an invalid password to see if the user is returned (it shouldn't be)
|
2022-04-21 18:54:53 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn find_user_wrong_password() {
|
|
|
|
let db = open_db().await;
|
|
|
|
|
|
|
|
new_user(&db).await;
|
|
|
|
|
2022-06-18 11:39:15 +00:00
|
|
|
let user = User::new(USERNAME, "wrong password 123!");
|
2022-04-21 18:54:53 +00:00
|
|
|
|
|
|
|
let err = db
|
2022-06-18 11:39:15 +00:00
|
|
|
.find_user(&user)
|
2022-04-21 18:54:53 +00:00
|
|
|
.await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert_eq!(err.to_string(), "user not found")
|
|
|
|
}
|
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
/// Test a search for a user who does not exist
|
2022-04-21 18:54:53 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn find_user_wrong_username() {
|
|
|
|
let db = open_db().await;
|
|
|
|
|
|
|
|
new_user(&db).await;
|
|
|
|
|
2022-06-18 11:39:15 +00:00
|
|
|
let user = User::new("not_exists_user", PASSWORD);
|
2022-04-21 18:54:53 +00:00
|
|
|
|
|
|
|
let err = db
|
2022-06-18 11:39:15 +00:00
|
|
|
.find_user(&user)
|
2022-04-21 18:54:53 +00:00
|
|
|
.await
|
|
|
|
.unwrap_err();
|
|
|
|
|
|
|
|
assert_eq!(err.to_string(), "user not found")
|
2022-04-19 19:08:13 +00:00
|
|
|
}
|
2022-04-23 21:52:54 +00:00
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
/// Test a search for a user by UUID who does not exist
|
2022-04-23 21:52:54 +00:00
|
|
|
#[tokio::test]
|
|
|
|
async fn find_user_wrong_id() {
|
|
|
|
let db = open_db().await;
|
|
|
|
|
|
|
|
new_user(&db).await;
|
|
|
|
|
2022-06-18 11:39:15 +00:00
|
|
|
let other_user = User::new("not_exists_user", "my secret passphrase");
|
2022-04-23 21:52:54 +00:00
|
|
|
|
2022-06-14 20:37:42 +00:00
|
|
|
let err = db.find_user_by_id(&other_user.id).await.unwrap_err();
|
2022-04-23 21:52:54 +00:00
|
|
|
|
|
|
|
assert_eq!(err.to_string(), "user not found")
|
|
|
|
}
|
2022-04-19 19:08:13 +00:00
|
|
|
}
|