tenebrous-sheets/src/db.rs

158 lines
5.0 KiB
Rust

use crate::models::characters::{Character, NewCharacter, StrippedCharacter};
use crate::models::users::{NewUser, User};
use sqlx::sqlite::{SqliteConnectOptions, SqlitePool, SqlitePoolOptions};
use sqlx::ConnectOptions;
use std::str::FromStr;
/// Type alias for the Rocket-managed singleton database connection.
pub type TenebrousDbConn<'a> = rocket::State<'a, SqlitePool>;
/// Create a connection pool to the database.
pub(crate) async fn create_pool(db_path: &str) -> Result<SqlitePool, crate::errors::Error> {
//Create database if missing.
let conn = SqliteConnectOptions::from_str(&format!("sqlite://{}", db_path))?
.create_if_missing(true)
.connect()
.await?;
drop(conn);
//Return actual conncetion pool.
SqlitePoolOptions::new()
.max_connections(5)
.connect(db_path)
.await
.map_err(|e| e.into())
}
#[rocket::async_trait]
pub(crate) trait Dao {
async fn load_user_by_id(&self, id: i32) -> sqlx::Result<Option<User>>;
async fn load_user(&self, for_username: &str) -> sqlx::Result<Option<User>>;
async fn insert_user(&self, new_user: NewUser<'_>) -> sqlx::Result<User>;
async fn load_character_list(&self, for_user_id: i32) -> sqlx::Result<Vec<StrippedCharacter>>;
async fn load_character(&self, character_id: i32) -> sqlx::Result<Option<Character>>;
async fn insert_character(&self, new_character: NewCharacter<'_>) -> sqlx::Result<()>;
async fn update_character<'a>(&self, character: &'a Character) -> sqlx::Result<()>;
async fn update_character_sheet<'a>(&self, character: &'a Character) -> sqlx::Result<()>;
}
#[rocket::async_trait]
impl Dao for SqlitePool {
async fn load_user_by_id(&self, user_id: i32) -> sqlx::Result<Option<User>> {
sqlx::query_as!(
User,
r#"SELECT id as "id: _", username, password FROM users WHERE id = ?"#,
user_id
)
.fetch_optional(self)
.await
}
async fn load_user(&self, for_username: &str) -> sqlx::Result<Option<User>> {
sqlx::query_as!(
User,
r#"SELECT id as "id: _", username, password FROM users WHERE username = ?"#,
for_username
)
.fetch_optional(self)
.await
}
async fn insert_user(&self, new_user: NewUser<'_>) -> sqlx::Result<User> {
sqlx::query("INSERT INTO users (username, password) values (?, ?)")
.bind(new_user.username)
.bind(new_user.password)
.execute(self)
.await?;
self.load_user(new_user.username)
.await
.and_then(|user| user.ok_or(sqlx::Error::RowNotFound))
}
async fn load_character_list(&self, for_user_id: i32) -> sqlx::Result<Vec<StrippedCharacter>> {
sqlx::query_as!(
StrippedCharacter,
r#"SELECT id as "id: _",
user_id as "user_id: _",
data_type as "data_type: _",
data_version as "data_version: _",
viewable, character_name
FROM characters WHERE user_id = ?"#,
for_user_id
)
.fetch_all(self)
.await
}
async fn load_character(&self, character_id: i32) -> sqlx::Result<Option<Character>> {
sqlx::query_as!(
Character,
r#"SELECT id as "id: _",
user_id as "user_id: _",
viewable, character_name, data,
data_type as "data_type: _",
data_version as "data_version: _"
FROM characters WHERE id = ?"#,
character_id
)
.fetch_optional(self)
.await
}
async fn insert_character(&self, new_character: NewCharacter<'_>) -> sqlx::Result<()> {
sqlx::query(
"INSERT INTO characters
(user_id, viewable, character_name, data_type, data_version, data)
values (?, ?, ?, ?, ?, ?)",
)
.bind(new_character.user_id)
.bind(new_character.viewable)
.bind(new_character.character_name)
.bind(new_character.data_type)
.bind(new_character.data_version)
.bind(new_character.data)
.execute(self)
.await?;
Ok(())
}
async fn update_character<'a>(&self, character: &'a Character) -> sqlx::Result<()> {
sqlx::query(
"UPDATE characters
set user_id = ?, viewable = ?, character_name = ?,
data_type = ?, data_version = ?, data = ? where id = ?",
)
.bind(character.user_id)
.bind(character.viewable)
.bind(&character.character_name)
.bind(character.data_type)
.bind(character.data_version)
.bind(&character.data)
.bind(character.id)
.execute(self)
.await?;
Ok(())
}
async fn update_character_sheet<'a>(&self, character: &'a Character) -> sqlx::Result<()> {
sqlx::query("UPDATE characters set data = ? where id = ?")
.bind(&character.data)
.bind(character.id)
.execute(self)
.await?;
Ok(())
}
}