379 lines
13 KiB
Rust
379 lines
13 KiB
Rust
use std::path::Path;
|
|
|
|
use async_std::channel::Receiver;
|
|
use include_dir::{include_dir, Dir};
|
|
use lazy_static::lazy_static;
|
|
use rusqlite::Connection;
|
|
use rusqlite_migration::Migrations;
|
|
|
|
use crate::{
|
|
database::{DatabaseResponse, Request},
|
|
types::{AccountState, FatalError, Game, User},
|
|
};
|
|
|
|
use super::{types::GameId, CharacterId, CharsheetRow, DatabaseRequest, SessionId, UserId};
|
|
|
|
static MIGRATIONS_DIR: Dir = include_dir!("$CARGO_MANIFEST_DIR/migrations");
|
|
|
|
lazy_static! {
|
|
static ref MIGRATIONS: Migrations<'static> =
|
|
Migrations::from_directory(&MIGRATIONS_DIR).unwrap();
|
|
}
|
|
|
|
pub struct DiskDb {
|
|
conn: Connection,
|
|
}
|
|
|
|
impl DiskDb {
|
|
pub fn new<P>(path: Option<P>) -> Result<Self, FatalError>
|
|
where
|
|
P: AsRef<Path>,
|
|
{
|
|
let mut conn = match path {
|
|
None => Connection::open(":memory:").expect("to create a memory connection"),
|
|
Some(path) => Connection::open(path).expect("to create connection"),
|
|
};
|
|
|
|
MIGRATIONS
|
|
.to_latest(&mut conn)
|
|
.map_err(|err| FatalError::DatabaseMigrationFailure(format!("{}", err)))?;
|
|
|
|
// setup_test_database(&conn)?;
|
|
|
|
Ok(DiskDb { conn })
|
|
}
|
|
|
|
pub fn user(&self, id: &UserId) -> Result<Option<User>, FatalError> {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("SELECT * FROM users WHERE uuid=?")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
let items: Vec<User> = stmt
|
|
.query_map([id.as_str()], |row| {
|
|
Ok(User {
|
|
id: row.get(0).unwrap(),
|
|
name: row.get(1).unwrap(),
|
|
password: row.get(2).unwrap(),
|
|
admin: row.get(3).unwrap(),
|
|
state: row.get(4).unwrap(),
|
|
})
|
|
})
|
|
.unwrap()
|
|
.collect::<Result<Vec<User>, rusqlite::Error>>()
|
|
.unwrap();
|
|
match &items[..] {
|
|
[] => Ok(None),
|
|
[item] => Ok(Some(item.clone())),
|
|
_ => Err(FatalError::NonUniqueDatabaseKey(id.as_str().to_owned())),
|
|
}
|
|
}
|
|
|
|
pub fn user_by_username(&self, username: &str) -> Result<Option<User>, FatalError> {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("SELECT * FROM users WHERE name=?")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
let items: Vec<User> = stmt
|
|
.query_map([username], |row| {
|
|
Ok(User {
|
|
id: row.get(0).unwrap(),
|
|
name: row.get(1).unwrap(),
|
|
password: row.get(2).unwrap(),
|
|
admin: row.get(3).unwrap(),
|
|
state: row.get(4).unwrap(),
|
|
})
|
|
})
|
|
.unwrap()
|
|
.collect::<Result<Vec<User>, rusqlite::Error>>()
|
|
.unwrap();
|
|
match &items[..] {
|
|
[] => Ok(None),
|
|
[item] => Ok(Some(item.clone())),
|
|
_ => Err(FatalError::NonUniqueDatabaseKey(username.to_owned())),
|
|
}
|
|
}
|
|
|
|
pub fn create_user(
|
|
&self,
|
|
name: &str,
|
|
password: &str,
|
|
admin: bool,
|
|
state: AccountState,
|
|
) -> Result<UserId, FatalError> {
|
|
let user_id = UserId::default();
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("INSERT INTO users VALUES (?, ?, ?, ?, ?)")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
stmt.execute((user_id.as_str(), name, password, admin, state))
|
|
.unwrap();
|
|
Ok(user_id)
|
|
}
|
|
|
|
pub fn save_user(&self, user: User) -> Result<UserId, FatalError> {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("UPDATE users SET name=?, password=?, admin=?, state=? WHERE uuid=?")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
stmt.execute((
|
|
user.name,
|
|
user.password,
|
|
user.admin,
|
|
user.state,
|
|
user.id.as_str(),
|
|
))
|
|
.unwrap();
|
|
Ok(user.id)
|
|
}
|
|
|
|
pub fn users(&self) -> Result<Vec<User>, FatalError> {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("SELECT * FROM users")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
let items = stmt
|
|
.query_map([], |row| {
|
|
Ok(User {
|
|
id: row.get(0).unwrap(),
|
|
name: row.get(1).unwrap(),
|
|
password: row.get(2).unwrap(),
|
|
admin: row.get(3).unwrap(),
|
|
state: row.get(4).unwrap(),
|
|
})
|
|
})
|
|
.unwrap()
|
|
.collect::<Result<Vec<User>, rusqlite::Error>>()
|
|
.unwrap();
|
|
Ok(items)
|
|
}
|
|
|
|
pub fn create_game(
|
|
&self,
|
|
gm: &UserId,
|
|
game_type: &str,
|
|
name: &str,
|
|
) -> Result<GameId, FatalError> {
|
|
let game_id = GameId::new();
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("INSERT INTO games VALUES (?, ?, ?, ?)")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
stmt.execute((game_id.as_str(), game_type, gm.as_str(), name))
|
|
.unwrap();
|
|
Ok(game_id)
|
|
}
|
|
|
|
pub fn save_game(&self, game: Game) -> Result<(), FatalError> {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("UPDATE games SET gm=? type_=? name=? WHERE id=?")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
stmt.execute((game.gm.as_str(), game.type_, game.name, game.id.as_str()))
|
|
.unwrap();
|
|
Ok(())
|
|
}
|
|
|
|
pub fn games(&self) -> Result<Vec<Game>, FatalError> {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("SELECT * FROM games")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
let items = stmt
|
|
.query_map([], |row| {
|
|
Ok(Game {
|
|
id: row.get(0).unwrap(),
|
|
type_: row.get(1).unwrap(),
|
|
gm: row.get(2).unwrap(),
|
|
name: row.get(3).unwrap(),
|
|
players: vec![],
|
|
})
|
|
})
|
|
.unwrap()
|
|
.collect::<Result<Vec<Game>, rusqlite::Error>>()
|
|
.unwrap();
|
|
Ok(items)
|
|
}
|
|
|
|
pub fn session(&self, session_id: &SessionId) -> Result<Option<User>, FatalError> {
|
|
let mut stmt = self.conn
|
|
.prepare("SELECT u.uuid, u.name, u.password, u.admin, u.enabled FROM sessions s INNER JOIN users u ON u.uuid = s.user_id WHERE s.id = ?")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
|
|
let items: Vec<User> = stmt
|
|
.query_map([session_id.as_str()], |row| {
|
|
Ok(User {
|
|
id: row.get(0).unwrap(),
|
|
name: row.get(1).unwrap(),
|
|
password: row.get(2).unwrap(),
|
|
admin: row.get(3).unwrap(),
|
|
state: row.get(4).unwrap(),
|
|
})
|
|
})
|
|
.unwrap()
|
|
.collect::<Result<Vec<User>, rusqlite::Error>>()
|
|
.unwrap();
|
|
match &items[..] {
|
|
[] => Ok(None),
|
|
[item] => Ok(Some(item.clone())),
|
|
_ => Err(FatalError::NonUniqueDatabaseKey(
|
|
session_id.as_str().to_owned(),
|
|
)),
|
|
}
|
|
}
|
|
|
|
fn create_session(&self, user_id: &UserId) -> Result<SessionId, FatalError> {
|
|
match self.user(user_id) {
|
|
Ok(Some(_)) => {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("INSERT INTO sessions VALUES (?, ?)")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
|
|
let session_id = SessionId::new();
|
|
stmt.execute((session_id.as_str(), user_id.as_str()))
|
|
.unwrap();
|
|
Ok(session_id)
|
|
}
|
|
Ok(None) => Err(FatalError::DatabaseKeyMissing),
|
|
Err(err) => Err(err),
|
|
}
|
|
}
|
|
|
|
pub fn character(&self, id: CharacterId) -> Result<Option<CharsheetRow>, FatalError> {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("SELECT uuid, game, data FROM characters WHERE uuid=?")
|
|
.map_err(|err| FatalError::ConstructQueryFailure(format!("{}", err)))?;
|
|
let items: Vec<CharsheetRow> = stmt
|
|
.query_map([id.as_str()], |row| {
|
|
let data: String = row.get(2).unwrap();
|
|
Ok(CharsheetRow {
|
|
id: row.get(0).unwrap(),
|
|
game: row.get(1).unwrap(),
|
|
data: serde_json::from_str(&data).unwrap(),
|
|
})
|
|
})
|
|
.unwrap()
|
|
.collect::<Result<Vec<CharsheetRow>, rusqlite::Error>>()
|
|
.unwrap();
|
|
match &items[..] {
|
|
[] => Ok(None),
|
|
[item] => Ok(Some(item.clone())),
|
|
_ => Err(FatalError::NonUniqueDatabaseKey(id.as_str().to_owned())),
|
|
}
|
|
}
|
|
|
|
pub fn save_character(
|
|
&self,
|
|
char_id: Option<CharacterId>,
|
|
game: GameId,
|
|
character: serde_json::Value,
|
|
) -> std::result::Result<CharacterId, FatalError> {
|
|
match char_id {
|
|
None => {
|
|
let char_id = CharacterId::new();
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("INSERT INTO characters VALUES (?, ?, ?)")
|
|
.unwrap();
|
|
stmt.execute((char_id.as_str(), game.as_str(), character.to_string()))
|
|
.unwrap();
|
|
|
|
Ok(char_id)
|
|
}
|
|
Some(char_id) => {
|
|
let mut stmt = self
|
|
.conn
|
|
.prepare("UPDATE characters SET data=? WHERE uuid=?")
|
|
.unwrap();
|
|
stmt.execute((character.to_string(), char_id.as_str()))
|
|
.unwrap();
|
|
|
|
Ok(char_id)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub async fn db_handler(db: DiskDb, requestor: Receiver<DatabaseRequest>) {
|
|
while let Ok(DatabaseRequest { tx, req }) = requestor.recv().await {
|
|
match req {
|
|
Request::Charsheet(id) => {
|
|
let sheet = db.character(id);
|
|
match sheet {
|
|
Ok(sheet) => {
|
|
tx.send(DatabaseResponse::Charsheet(sheet)).await.unwrap();
|
|
}
|
|
_ => unimplemented!("errors for Charsheet"),
|
|
}
|
|
}
|
|
Request::CreateUser(_, _, _, _) => {}
|
|
Request::CreateGame(_, _, _) => {}
|
|
Request::CreateSession(id) => {
|
|
let session_id = db.create_session(&id).unwrap();
|
|
tx.send(DatabaseResponse::CreateSession(session_id))
|
|
.await
|
|
.unwrap();
|
|
}
|
|
Request::Games => match db.games() {
|
|
Ok(games) => tx.send(DatabaseResponse::Games(games)).await.unwrap(),
|
|
_ => unimplemented!("errors for Request::Games"),
|
|
},
|
|
Request::Game(_game_id) => {
|
|
unimplemented!("Request::Game handler");
|
|
}
|
|
Request::SaveGame(game) => {
|
|
let id = game.id.clone();
|
|
let save_result = db.save_game(game);
|
|
match save_result {
|
|
Ok(_) => {
|
|
tx.send(DatabaseResponse::SaveGame(id)).await.unwrap();
|
|
}
|
|
err => panic!("{:?}", err),
|
|
}
|
|
}
|
|
Request::User(uid) => {
|
|
let user = db.user(&uid);
|
|
match user {
|
|
Ok(user) => {
|
|
tx.send(DatabaseResponse::User(user)).await.unwrap();
|
|
}
|
|
err => panic!("{:?}", err),
|
|
}
|
|
}
|
|
Request::UserByUsername(username) => {
|
|
let user = db.user_by_username(&username);
|
|
match user {
|
|
Ok(user) => tx.send(DatabaseResponse::User(user)).await.unwrap(),
|
|
err => panic!("{:?}", err),
|
|
}
|
|
}
|
|
Request::SaveUser(user) => {
|
|
let user_id = db.save_user(user);
|
|
match user_id {
|
|
Ok(user_id) => {
|
|
tx.send(DatabaseResponse::SaveUser(user_id)).await.unwrap();
|
|
}
|
|
err => panic!("{:?}", err),
|
|
}
|
|
}
|
|
Request::Session(session_id) => {
|
|
let user = db.session(&session_id);
|
|
match user {
|
|
Ok(user) => tx.send(DatabaseResponse::Session(user)).await.unwrap(),
|
|
err => panic!("{:?}", err),
|
|
}
|
|
}
|
|
Request::Users => {
|
|
let users = db.users();
|
|
match users {
|
|
Ok(users) => {
|
|
tx.send(DatabaseResponse::Users(users)).await.unwrap();
|
|
}
|
|
_ => unimplemented!("request::Users"),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|