// use sqlx::{pool::PoolConnection, prelude::*, query, query_as, SqliteConnection, SqlitePool}; // use tokio::stream::StreamExt; use anyhow::Result; use fallible_iterator::FallibleIterator; use rusqlite::{params, Connection, Error as SqliteError, ErrorCode}; use server_client::encapsulate; use tokio::task::spawn; use uuid::Uuid; use std::convert::TryInto; pub async fn start() -> DbClient { let mut server = DbServer::new(Db::new(Connection::open_in_memory().unwrap())); let mut client = server.connection(); spawn(async move { loop { server.tick().await; tokio::task::yield_now().await } }); client.load_setup().await; client } pub struct Db { conn: Connection, } impl Db { fn new(conn: Connection) -> Self { Self { conn } } } // type LobbyId = (i32,); #[encapsulate(pub async ordered)] impl Db { pub fn load_setup(&mut self) { self.conn.execute_batch(include_str!("setup.sql")).unwrap() } pub fn add_user(&mut self, name: String) -> Result { loop { let uuid = Uuid::new_v4(); // println!("{:?}", uuid.as_bytes().to_vec()); let res = self.conn.execute( "INSERT INTO Users(uuid, name) VALUES(?, ?)", params![uuid.as_bytes().to_vec(), name.to_string()], ); // Server crashes if uuids collide if let Err(e) = res { match e { SqliteError::SqliteFailure( rusqlite::ffi::Error { code: ErrorCode::ConstraintViolation, extended_code: _, }, _, ) => { log::error!("add_user db error (constraint violation): {}", e); return Err(e.into()); } e => { log::error!("add_user db error: {}", e); return Err(e.into()); } } } return Ok(uuid); } } // FIXME: return Results intead of crashing pub fn get_users_in_lobby_where_user_is(&mut self, uuid: Uuid) -> Vec { let mut prepared = self.conn.prepare_cached("SELECT Name FROM Users WHERE UUID in (SELECT UserId FROM UsersInLobbies WHERE LobbyId = (SELECT LobbyId FROM UsersInLobbies WHERE UserId = ?))").unwrap(); prepared .query(params![uuid.as_bytes().to_vec()]) .and_then(|r| r.map(|r| r.get(0)).collect()) .unwrap() } // FIXME: return Results intead of crashing pub fn get_uuids_in_lobby_where_user_is(&mut self, uuid: Uuid) -> Vec { let mut prepared = self.conn.prepare_cached("SELECT UUID FROM Users WHERE UUID in (SELECT UserId FROM UsersInLobbies WHERE LobbyId = (SELECT LobbyId FROM UsersInLobbies WHERE UserId = ?))").unwrap(); prepared .query(params![uuid.as_bytes().to_vec()]) .and_then(|r| { r.map(|r| r.get::<_, Vec>(0)) .map(|x| Ok(Uuid::from_slice(&x).unwrap())) .collect() }) .unwrap() } // FIXME: return Results intead of crashing pub fn get_name_for_uuid(&mut self, uuid: Uuid) -> String { let mut prepared = self .conn .prepare_cached("SELECT Name FROM Users WHERE UUID = ?") .unwrap(); prepared .query(params![uuid.as_bytes().to_vec()]) .and_then(|mut r| r.next().and_then(|r| r.unwrap().get::<_, String>(0))) .unwrap() } // FIXME: return Results intead of crashing pub fn create_lobby(&mut self, public: bool) -> u32 { let id = rand::random(); log::info!("Created the lobby {}", id); self.conn .execute( "INSERT INTO Lobbies(id, public) VALUES(?, ?)", params![id as i32, public], ) .unwrap(); // Server crashes if ids collide id } // FIXME: return Results intead of crashing pub fn join_lobby(&mut self, user: Uuid, lobby: u32) { self.conn .execute( "INSERT INTO UsersInLobbies(UserId, LobbyId) VALUES(?, ?)", params![user.as_bytes().to_vec(), lobby as i32], ) .unwrap(); // Server crashes if ids collide log::info!("{} joined the lobby {}", user, lobby); } // FIXME: return Results intead of crashing pub fn leave_lobby(&mut self, user: Uuid) { log::info!("{} leaving the lobby", user); self.delete_vote(user); let v = self.get_lobby_for_user(user); self.conn .execute( "DELETE FROM UsersInLobbies WHERE UserId = ?", params![user.as_bytes().to_vec()], ) .unwrap(); if let Some(id) = v { let lines = self.conn.execute("DELETE FROM Lobbies WHERE 0 = (SELECT count(*) FROM UsersInLobbies WHERE LobbyID = ?)", params![id]).unwrap(); log::info!("Deleted {} lobbies ({})", lines, id as u32) } } // FIXME: return Results intead of crashing pub fn disconnect(&mut self, user: Uuid) { self.leave_lobby(user); log::info!("{} disconnecting", user); self.conn .execute( "DELETE FROM Users WHERE UUID = ?", params![user.as_bytes().to_vec()], ) .unwrap(); // log::error!("Disconnect DB result{:?}", r); } // FIXME: return Results intead of crashing pub fn get_lobby_for_user(&mut self, user: Uuid) -> Option { match self.conn.query_row( "SELECT LobbyID from UsersInLobbies WHERE UserID = ?", params![user.as_bytes().to_vec()], |r| r.get::<_, i32>(0).map(|x| x as u32), ) { Ok(v) => Ok(Some(v)), Err(e) => match e { SqliteError::QueryReturnedNoRows => Ok(None), e => Err(e), }, } .unwrap() } // FIXME: return Results intead of crashing pub fn get_public_lobbies(&mut self) -> Vec { let mut prepared = self .conn .prepare("SELECT ID FROM Lobbies WHERE Public = TRUE") .unwrap(); prepared .query(params![]) .and_then(|r| r.map(|r| r.get::<_, i32>(0).map(|x| x as u32)).collect()) .unwrap() } // FIXME: return Results intead of crashing fn delete_vote(&mut self, user: Uuid) { self.conn .execute( "DELETE FROM Votes WHERE UserId = ?", params![user.as_bytes().to_vec()], ) .unwrap(); } // FIXME: return Results intead of crashing pub fn vote(&mut self, user: Uuid, game: u32) { self.delete_vote(user); self.conn .execute( "INSERT INTO Votes(UserID, GameID) VALUES(?, ?)", params![user.as_bytes().to_vec(), game as i32], ) .unwrap(); } // FIXME: return Results intead of crashing pub fn vote_ready(&mut self, user: Uuid) { // self.delete_vote(user); self.conn .execute( "UPDATE Votes SET Ready = TRUE WHERE UserID = ?", params![user.as_bytes().to_vec()], ) .unwrap(); } // FIXME: return Results intead of crashing pub fn get_votes(&mut self, lobby: u32) -> Vec<(String, u32, bool)> { let mut prepared = self.conn.prepare_cached("SELECT Users.Name, Votes.GameID, Votes.Ready FROM Votes JOIN Users ON Users.UUID = Votes.UserID WHERE Votes.UserID IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?)").unwrap(); prepared .query(params![lobby as i32]) .and_then(|r| { r.map(|r| { r.try_into() .map(|(s, n, b): (String, i32, bool)| (s, n as u32, b)) }) .collect() }) .unwrap() } // FIXME: return Results intead of crashing pub fn is_poll_finished(&mut self, lobby: u32) -> bool { self.conn.query_row( "SELECT (SELECT COUNT(*) FROM UsersInLobbies where LobbyID = ?) = (SELECT COUNT(*) FROM Votes WHERE UserID IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?) AND Ready = TRUE)", params![lobby as i32, lobby as i32], |r| r.get(0) ).unwrap() } // FIXME: return Results intead of crashing pub fn delete_votes(&mut self, lobby: u32) { self.conn .execute("DELETE FROM Votes WHERE UserId IN (SELECT UserID FROM UsersInLobbies WHERE LobbyID = ?)", params![lobby as i32]).unwrap(); } }