Changing to postgres
This commit is contained in:
parent
dbd52f31c6
commit
2dcfb991b4
6
.vscode/settings.json
vendored
Normal file
6
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"rust-analyzer.linkedProjects": [
|
||||||
|
"./Cargo.toml",
|
||||||
|
"./Cargo.toml"
|
||||||
|
]
|
||||||
|
}
|
|
@ -6,7 +6,10 @@ edition = "2021"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
chrono = { version = "0.4.24", features = ["serde"] }
|
||||||
mysql = "22.2.0"
|
|
||||||
mysql_common = { version = "0.28.2", features = ["chrono"] }
|
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
|
rocket = { version = "0.5.0-rc.2", features = ["json"] }
|
||||||
|
tokio = { version = "1.27.0", features = ["full"] }
|
||||||
|
tokio-postgres = { version = "0.7.8", features = ["with-chrono-0_4"] }
|
||||||
|
tokio-test = "0.4.2"
|
||||||
|
serde = "1.0.154"
|
|
@ -1,123 +0,0 @@
|
||||||
use mysql_common::{chrono::NaiveDateTime, frunk::HList};
|
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub type MapRow = HList!(
|
|
||||||
String,
|
|
||||||
String,
|
|
||||||
i8,
|
|
||||||
i8,
|
|
||||||
String,
|
|
||||||
NaiveDateTime,
|
|
||||||
String,
|
|
||||||
i32,
|
|
||||||
i32,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool,
|
|
||||||
bool
|
|
||||||
);
|
|
||||||
|
|
||||||
// Different tile types and whether they appear in the map.
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
pub struct MapTileData {
|
|
||||||
pub death: bool,
|
|
||||||
pub through: bool,
|
|
||||||
pub jump: bool,
|
|
||||||
pub dfreeze: bool,
|
|
||||||
pub ehook_start: bool,
|
|
||||||
pub hit_end: bool,
|
|
||||||
pub solo_start: bool,
|
|
||||||
pub tele_gun: bool,
|
|
||||||
pub tele_grenade: bool,
|
|
||||||
pub tele_laser: bool,
|
|
||||||
pub npc_start: bool,
|
|
||||||
pub super_start: bool,
|
|
||||||
pub jetpack_start: bool,
|
|
||||||
pub walljump: bool,
|
|
||||||
pub nph_start: bool,
|
|
||||||
pub weapon_shotgun: bool,
|
|
||||||
pub weapon_grenade: bool,
|
|
||||||
pub powerup_ninja: bool,
|
|
||||||
pub weapon_rifle: bool,
|
|
||||||
pub laser_stop: bool,
|
|
||||||
pub crazy_shotgun: bool,
|
|
||||||
pub dragger: bool,
|
|
||||||
pub door: bool,
|
|
||||||
pub switch_timed: bool,
|
|
||||||
pub switch: bool,
|
|
||||||
pub stop: bool,
|
|
||||||
pub through_all: bool,
|
|
||||||
pub tune: bool,
|
|
||||||
pub oldlaser: bool,
|
|
||||||
pub teleinevil: bool,
|
|
||||||
pub telein: bool,
|
|
||||||
pub telecheck: bool,
|
|
||||||
pub teleinweapon: bool,
|
|
||||||
pub teleinhook: bool,
|
|
||||||
pub checkpoint_first: bool,
|
|
||||||
pub bonus: bool,
|
|
||||||
pub boost: bool,
|
|
||||||
pub plasmaf: bool,
|
|
||||||
pub plasmae: bool,
|
|
||||||
pub plasmau: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
pub struct Map {
|
|
||||||
pub name: String,
|
|
||||||
pub server: String,
|
|
||||||
pub points: i8,
|
|
||||||
pub stars: i8,
|
|
||||||
pub mapper: String,
|
|
||||||
pub timestamp: NaiveDateTime,
|
|
||||||
pub width: i32,
|
|
||||||
pub height: i32,
|
|
||||||
pub tile_data: MapTileData,
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
IMPORTANT: For this to work the following SQL query must be executed:
|
|
||||||
```SQL
|
|
||||||
UPDATE record_maps SET Timestamp='1970-01-01 01:01:01' WHERE Timestamp='1990-01-01 00:00:00';
|
|
||||||
```
|
|
||||||
Else the reads on `record_maps` will fail whenever the timestamp is '0000-00-00 00:00:00'!
|
|
||||||
TODO: Fix this server-side.
|
|
||||||
*/
|
|
|
@ -1,256 +1,148 @@
|
||||||
use mysql::prelude::*;
|
use std::{env, error::Error};
|
||||||
use mysql::*;
|
|
||||||
use mysql_common::frunk::hlist_pat;
|
|
||||||
use std::error::Error;
|
|
||||||
use std::result::Result;
|
|
||||||
use std::{env, result};
|
|
||||||
|
|
||||||
pub mod map;
|
use dotenv::dotenv;
|
||||||
pub mod race;
|
use tokio_postgres::{Client, NoTls};
|
||||||
pub mod teamrace;
|
|
||||||
|
|
||||||
pub fn create_pool() -> Pool {
|
use self::models::map::Map;
|
||||||
let url = match env::var("DB_URI") {
|
|
||||||
Ok(uri) => uri,
|
mod models;
|
||||||
Err(err) => {
|
|
||||||
println!("You must provide an env var: 'DB_URI'!");
|
pub struct DatabaseHandler {
|
||||||
panic!("{}", err);
|
pub client: Client,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DatabaseHandler {
|
||||||
|
pub async fn create() -> Result<DatabaseHandler, Box<dyn Error>> {
|
||||||
|
// Load the env file
|
||||||
|
dotenv()?;
|
||||||
|
|
||||||
|
// Load in the environment variables
|
||||||
|
let connection_host = env::var("DB_HOST")?;
|
||||||
|
let connection_user = env::var("DB_USER")?;
|
||||||
|
let connection_db = env::var("DB_NAME")?;
|
||||||
|
|
||||||
|
let connection_string = format!(
|
||||||
|
"host={} user={} dbname={}",
|
||||||
|
connection_host, connection_user, connection_db
|
||||||
|
);
|
||||||
|
|
||||||
|
// Connect to the database
|
||||||
|
let (client, connection) = tokio_postgres::connect(&connection_string, NoTls).await?;
|
||||||
|
|
||||||
|
// NOTE: This comes directly from the official documentation (https://docs.rs/tokio-postgres/latest/tokio_postgres/#example)
|
||||||
|
// I hace no idea what in the flying flop it means...
|
||||||
|
tokio::spawn(async move {
|
||||||
|
if let Err(e) = connection.await {
|
||||||
|
eprintln!("Error while connecting to the database: {}", e);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(DatabaseHandler { client })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_all_maps(&self) -> Result<Vec<Map>, Box<dyn Error>> {
|
||||||
|
// FIXME: Why does this have to be mutable? Should fix.
|
||||||
|
let mut results = self
|
||||||
|
.client
|
||||||
|
.query(
|
||||||
|
"
|
||||||
|
SELECT record_maps.map, type, points, stars, mapper, release, width, height,
|
||||||
|
CONCAT_WS(',',
|
||||||
|
CASE WHEN DEATH = '1' THEN 'DEATH' END,
|
||||||
|
CASE WHEN THROUGH = '1' THEN 'THROUGH' END,
|
||||||
|
CASE WHEN JUMP = '1' THEN 'JUMP' END,
|
||||||
|
CASE WHEN DFREEZE = '1' THEN 'DFREEZE' END,
|
||||||
|
CASE WHEN EHOOK_START = '1' THEN 'EHOOK_START' END,
|
||||||
|
CASE WHEN HIT_END = '1' THEN 'HIT_END' END,
|
||||||
|
CASE WHEN SOLO_START = '1' THEN 'SOLO_START' END,
|
||||||
|
CASE WHEN TELE_GUN = '1' THEN 'TELE_GUN' END,
|
||||||
|
CASE WHEN TELE_GRENADE = '1' THEN 'TELE_GRENADE' END,
|
||||||
|
CASE WHEN TELE_LASER = '1' THEN 'TELE_LASER' END,
|
||||||
|
CASE WHEN NPC_START = '1' THEN 'NPC_START' END,
|
||||||
|
CASE WHEN SUPER_START = '1' THEN 'SUPER_START' END,
|
||||||
|
CASE WHEN JETPACK_START = '1' THEN 'JETPACK_START' END,
|
||||||
|
CASE WHEN WALLJUMP = '1' THEN 'WALLJUMP' END,
|
||||||
|
CASE WHEN NPH_START = '1' THEN 'NPH_START' END,
|
||||||
|
CASE WHEN WEAPON_SHOTGUN = '1' THEN 'WEAPON_SHOTGUN' END,
|
||||||
|
CASE WHEN WEAPON_GRENADE = '1' THEN 'WEAPON_GRENADE' END,
|
||||||
|
CASE WHEN POWERUP_NINJA = '1' THEN 'POWERUP_NINJA' END,
|
||||||
|
CASE WHEN WEAPON_RIFLE = '1' THEN 'WEAPON_RIFLE' END,
|
||||||
|
CASE WHEN LASER_STOP = '1' THEN 'LASER_STOP' END,
|
||||||
|
CASE WHEN CRAZY_SHOTGUN = '1' THEN 'CRAZY_SHOTGUN' END,
|
||||||
|
CASE WHEN DRAGGER = '1' THEN 'DRAGGER' END,
|
||||||
|
CASE WHEN DOOR = '1' THEN 'DOOR' END,
|
||||||
|
CASE WHEN SWITCH_TIMED = '1' THEN 'SWITCH_TIMED' END,
|
||||||
|
CASE WHEN SWITCH = '1' THEN 'SWITCH' END,
|
||||||
|
CASE WHEN STOP = '1' THEN 'STOP' END,
|
||||||
|
CASE WHEN THROUGH_ALL = '1' THEN 'THROUGH_ALL' END,
|
||||||
|
CASE WHEN TUNE = '1' THEN 'TUNE' END,
|
||||||
|
CASE WHEN OLDLASER = '1' THEN 'OLDLASER' END,
|
||||||
|
CASE WHEN TELEINEVIL = '1' THEN 'TELEINEVIL' END,
|
||||||
|
CASE WHEN TELEIN = '1' THEN 'TELEIN' END,
|
||||||
|
CASE WHEN TELECHECK = '1' THEN 'TELECHECK' END,
|
||||||
|
CASE WHEN TELEINWEAPON = '1' THEN 'TELEINWEAPON' END,
|
||||||
|
CASE WHEN TELEINHOOK = '1' THEN 'TELEINHOOK' END,
|
||||||
|
CASE WHEN CHECKPOINT_FIRST = '1' THEN 'CHECKPOINT_FIRST' END,
|
||||||
|
CASE WHEN BONUS = '1' THEN 'BONUS' END,
|
||||||
|
CASE WHEN BOOST = '1' THEN 'BOOST' END,
|
||||||
|
CASE WHEN PLASMAF = '1' THEN 'PLASMAF' END,
|
||||||
|
CASE WHEN PLASMAE = '1' THEN 'PLASMAE' END,
|
||||||
|
CASE WHEN PLASMAU = '1' THEN 'PLASMAU' END) AS tiles
|
||||||
|
FROM record_maps JOIN record_mapinfo ON record_maps.map = record_mapinfo.map
|
||||||
|
",
|
||||||
|
&[],
|
||||||
|
)
|
||||||
|
.await?
|
||||||
|
.into_iter()
|
||||||
|
.map(|row| Map::from_db_row(&row));
|
||||||
|
|
||||||
|
// If the result has errors, return it. Otherwise, return all the rows.
|
||||||
|
match results.find(|row| row.is_err()) {
|
||||||
|
Some(row) => row.map(|row| vec![row]),
|
||||||
|
None => Ok(results.map(|row| row.unwrap()).collect()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_database_connection() {
|
||||||
|
async fn test() {
|
||||||
|
let db = match DatabaseHandler::create().await {
|
||||||
|
Ok(db) => db,
|
||||||
|
Err(err) => panic!("Could not get a client!\n{}", err),
|
||||||
};
|
};
|
||||||
|
|
||||||
let pool = match Pool::new(url.as_str()) {
|
let msg = "Hello World!";
|
||||||
Ok(pool) => pool,
|
let rows = match db.client.query("SELECT $1::TEXT", &[&msg]).await {
|
||||||
Err(err) => {
|
Ok(rows) => rows,
|
||||||
println!("Couldn't connect to the database!");
|
Err(err) => panic!("Could not create query!\n{}", err),
|
||||||
panic!("{}", err);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pool
|
let value: &str = rows[0].get(0);
|
||||||
}
|
assert_eq!(value, msg)
|
||||||
|
|
||||||
pub struct DatabasePoolStore(pub Pool);
|
|
||||||
|
|
||||||
pub fn get_maps(pool: &Pool) -> Result<Vec<map::Map>, Box<dyn Error>> {
|
|
||||||
let mut conn = pool.get_conn()?;
|
|
||||||
|
|
||||||
let maps = conn.query_map(
|
|
||||||
"SELECT * FROM record_maps AS maps JOIN record_mapinfo AS mapinfo ON maps.Map = mapinfo.Map",
|
|
||||||
|row: map::MapRow| {
|
|
||||||
let hlist_pat![ name, server, points, stars, mapper, timestamp, _name_again, width, height, death, through, jump, dfreeze, hit_end, ehook_start, solo_start, tele_gun, tele_grenade, tele_laser, npc_start, super_start, jetpack_start, walljump, nph_start, weapon_shotgun, weapon_grenade, powerup_ninja, weapon_rifle, laser_stop, crazy_shotgun, dragger, door, switch_timed, switch, stop, through_all, tune, oldlaser, teleinevil, telein, telecheck, teleinweapon, teleinhook, checkpoint_first, bonus, boost, plasmaf, plasmae, plasmau ] = row;
|
|
||||||
map::Map {
|
|
||||||
name,
|
|
||||||
server, points,
|
|
||||||
stars, mapper,
|
|
||||||
timestamp,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
tile_data: map::MapTileData { death, through, jump, dfreeze, hit_end, ehook_start, solo_start, tele_gun, tele_grenade, tele_laser, npc_start, super_start, jetpack_start, walljump, nph_start, weapon_shotgun, weapon_grenade, powerup_ninja, weapon_rifle, laser_stop, crazy_shotgun, dragger, door, switch_timed, switch, stop, through_all, tune, oldlaser, teleinevil, telein, telecheck, teleinweapon, teleinhook, checkpoint_first, bonus, boost, plasmaf, plasmae, plasmau }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokio_test::block_on(test())
|
||||||
}
|
}
|
||||||
)?;
|
|
||||||
|
|
||||||
Ok(maps)
|
#[test]
|
||||||
}
|
fn test_get_all_maps() {
|
||||||
|
async fn test() {
|
||||||
|
let db = match DatabaseHandler::create().await {
|
||||||
|
Ok(db) => db,
|
||||||
|
Err(err) => panic!("Could not get a client!\n{}", err),
|
||||||
|
};
|
||||||
|
|
||||||
pub fn get_map_by_name(pool: &Pool, name: &str) -> Result<Option<map::Map>, Box<dyn Error>> {
|
match db.get_all_maps().await {
|
||||||
let mut conn = pool.get_conn()?;
|
Ok(maps) => println!("Found maps: {:?}", maps.len()),
|
||||||
let stmt = conn.prep("SELECT * FROM record_maps AS maps JOIN record_mapinfo AS mapinfo ON maps.Map = mapinfo.Map WHERE maps.Map = ?")?;
|
Err(err) => panic!("Could not get all maps!\n{}", err),
|
||||||
|
};
|
||||||
let map: Option<map::MapRow> = conn.exec_first(&stmt, (name,))?;
|
|
||||||
|
|
||||||
Ok(match map {
|
|
||||||
Some(row) => {
|
|
||||||
let hlist_pat![
|
|
||||||
name,
|
|
||||||
server,
|
|
||||||
points,
|
|
||||||
stars,
|
|
||||||
mapper,
|
|
||||||
timestamp,
|
|
||||||
_name_again,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
death,
|
|
||||||
through,
|
|
||||||
jump,
|
|
||||||
dfreeze,
|
|
||||||
hit_end,
|
|
||||||
ehook_start,
|
|
||||||
solo_start,
|
|
||||||
tele_gun,
|
|
||||||
tele_grenade,
|
|
||||||
tele_laser,
|
|
||||||
npc_start,
|
|
||||||
super_start,
|
|
||||||
jetpack_start,
|
|
||||||
walljump,
|
|
||||||
nph_start,
|
|
||||||
weapon_shotgun,
|
|
||||||
weapon_grenade,
|
|
||||||
powerup_ninja,
|
|
||||||
weapon_rifle,
|
|
||||||
laser_stop,
|
|
||||||
crazy_shotgun,
|
|
||||||
dragger,
|
|
||||||
door,
|
|
||||||
switch_timed,
|
|
||||||
switch,
|
|
||||||
stop,
|
|
||||||
through_all,
|
|
||||||
tune,
|
|
||||||
oldlaser,
|
|
||||||
teleinevil,
|
|
||||||
telein,
|
|
||||||
telecheck,
|
|
||||||
teleinweapon,
|
|
||||||
teleinhook,
|
|
||||||
checkpoint_first,
|
|
||||||
bonus,
|
|
||||||
boost,
|
|
||||||
plasmaf,
|
|
||||||
plasmae,
|
|
||||||
plasmau
|
|
||||||
] = row;
|
|
||||||
Some(map::Map {
|
|
||||||
name,
|
|
||||||
server,
|
|
||||||
points,
|
|
||||||
stars,
|
|
||||||
mapper,
|
|
||||||
timestamp,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
tile_data: map::MapTileData {
|
|
||||||
death,
|
|
||||||
through,
|
|
||||||
jump,
|
|
||||||
dfreeze,
|
|
||||||
hit_end,
|
|
||||||
ehook_start,
|
|
||||||
solo_start,
|
|
||||||
tele_gun,
|
|
||||||
tele_grenade,
|
|
||||||
tele_laser,
|
|
||||||
npc_start,
|
|
||||||
super_start,
|
|
||||||
jetpack_start,
|
|
||||||
walljump,
|
|
||||||
nph_start,
|
|
||||||
weapon_shotgun,
|
|
||||||
weapon_grenade,
|
|
||||||
powerup_ninja,
|
|
||||||
weapon_rifle,
|
|
||||||
laser_stop,
|
|
||||||
crazy_shotgun,
|
|
||||||
dragger,
|
|
||||||
door,
|
|
||||||
switch_timed,
|
|
||||||
switch,
|
|
||||||
stop,
|
|
||||||
through_all,
|
|
||||||
tune,
|
|
||||||
oldlaser,
|
|
||||||
teleinevil,
|
|
||||||
telein,
|
|
||||||
telecheck,
|
|
||||||
teleinweapon,
|
|
||||||
teleinhook,
|
|
||||||
checkpoint_first,
|
|
||||||
bonus,
|
|
||||||
boost,
|
|
||||||
plasmaf,
|
|
||||||
plasmae,
|
|
||||||
plasmau,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
None => None,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn races_result(row: race::RaceRow) -> race::Race {
|
tokio_test::block_on(test())
|
||||||
let hlist_pat![
|
|
||||||
map, name, timestamp, time, server, cp1, cp2, cp3, cp4, cp5, cp6, cp7, cp8, cp9, cp10,
|
|
||||||
cp11, cp12, cp13, cp14, cp15, cp16, cp17, cp18, cp19, cp20, cp21, cp22, cp23, cp24, cp25,
|
|
||||||
gameid, ddnet7
|
|
||||||
] = row;
|
|
||||||
race::Race {
|
|
||||||
map,
|
|
||||||
name,
|
|
||||||
timestamp,
|
|
||||||
time,
|
|
||||||
server,
|
|
||||||
cp1,
|
|
||||||
cp2,
|
|
||||||
cp3,
|
|
||||||
cp4,
|
|
||||||
cp5,
|
|
||||||
cp6,
|
|
||||||
cp7,
|
|
||||||
cp8,
|
|
||||||
cp9,
|
|
||||||
cp10,
|
|
||||||
cp11,
|
|
||||||
cp12,
|
|
||||||
cp13,
|
|
||||||
cp14,
|
|
||||||
cp15,
|
|
||||||
cp16,
|
|
||||||
cp17,
|
|
||||||
cp18,
|
|
||||||
cp19,
|
|
||||||
cp20,
|
|
||||||
cp21,
|
|
||||||
cp22,
|
|
||||||
cp23,
|
|
||||||
cp24,
|
|
||||||
cp25,
|
|
||||||
gameid,
|
|
||||||
ddnet7,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_races_by_player(pool: &Pool, player: &str) -> Result<Vec<race::Race>, Box<dyn Error>> {
|
|
||||||
let mut conn = pool.get_conn()?;
|
|
||||||
let stmt = conn.prep("SELECT * FROM record_race WHERE Name = ?")?;
|
|
||||||
|
|
||||||
let races = conn.exec_map(&stmt, (player,), races_result)?;
|
|
||||||
|
|
||||||
Ok(races)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_races_by_id(pool: &Pool, id: &str) -> Result<Vec<race::Race>, Box<dyn Error>> {
|
|
||||||
let mut conn = pool.get_conn()?;
|
|
||||||
let stmt = conn.prep("SELECT * FROM record_race WHERE GameID = ?")?;
|
|
||||||
|
|
||||||
let races = conn.exec_map(&stmt, (id,), races_result)?;
|
|
||||||
|
|
||||||
Ok(races)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_races_by_map(pool: &Pool, map: &str) -> Result<Vec<race::Race>, Box<dyn Error>> {
|
|
||||||
let mut conn = pool.get_conn()?;
|
|
||||||
let stmt = conn.prep("SELECT * FROM record_race WHERE Map = ?")?;
|
|
||||||
|
|
||||||
let races = conn.exec_map(&stmt, (map,), races_result)?;
|
|
||||||
|
|
||||||
Ok(races)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_player_total_playtime(
|
|
||||||
pool: &Pool,
|
|
||||||
player: &str,
|
|
||||||
) -> Result<(Option<f64>, Option<i32>), Box<dyn Error>> {
|
|
||||||
let mut conn = pool.get_conn()?;
|
|
||||||
let stmt = conn.prep(
|
|
||||||
"SELECT COALESCE(SUM(Time),0), COALESCE(COUNT(Time),0) FROM record_race WHERE Name = ?",
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let result = conn.exec_first(&stmt, (player,))?;
|
|
||||||
|
|
||||||
match result {
|
|
||||||
Some((total_playtime, total_games)) => Ok((total_playtime, total_games)),
|
|
||||||
None => Ok((None::<f64>, None::<i32>)),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
43
src/database/models/map.rs
Normal file
43
src/database/models/map.rs
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use chrono::NaiveDateTime;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio_postgres::Row;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Map {
|
||||||
|
map: String,
|
||||||
|
mapper: String,
|
||||||
|
category: String,
|
||||||
|
points: u8,
|
||||||
|
stars: u8,
|
||||||
|
release: Option<NaiveDateTime>,
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
tiles: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Map {
|
||||||
|
pub fn from_db_row(db_row: &Row) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let map: String = db_row.try_get(0)?;
|
||||||
|
let category: String = db_row.try_get(1)?;
|
||||||
|
let points_i16: i16 = db_row.try_get(2)?;
|
||||||
|
let points: u8 = points_i16 as u8;
|
||||||
|
let stars_i16: i16 = db_row.try_get(3)?;
|
||||||
|
let stars: u8 = stars_i16 as u8;
|
||||||
|
let mapper: String = db_row.try_get(4)?;
|
||||||
|
let release: Option<NaiveDateTime> = db_row.try_get(5)?;
|
||||||
|
|
||||||
|
Ok(Map {
|
||||||
|
map,
|
||||||
|
mapper,
|
||||||
|
category,
|
||||||
|
points,
|
||||||
|
stars,
|
||||||
|
release,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
tiles: vec![],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
1
src/database/models/mod.rs
Normal file
1
src/database/models/mod.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pub mod map;
|
|
@ -1,74 +0,0 @@
|
||||||
use mysql_common::{chrono::NaiveDateTime, frunk::HList};
|
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub type RaceRow = HList!(
|
|
||||||
String,
|
|
||||||
String,
|
|
||||||
NaiveDateTime,
|
|
||||||
f64,
|
|
||||||
String,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
f64,
|
|
||||||
Option<String>,
|
|
||||||
bool
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
pub struct Race {
|
|
||||||
pub map: String,
|
|
||||||
pub name: String,
|
|
||||||
pub timestamp: NaiveDateTime,
|
|
||||||
pub time: f64,
|
|
||||||
pub server: String,
|
|
||||||
pub cp1: f64,
|
|
||||||
pub cp2: f64,
|
|
||||||
pub cp3: f64,
|
|
||||||
pub cp4: f64,
|
|
||||||
pub cp5: f64,
|
|
||||||
pub cp6: f64,
|
|
||||||
pub cp7: f64,
|
|
||||||
pub cp8: f64,
|
|
||||||
pub cp9: f64,
|
|
||||||
pub cp10: f64,
|
|
||||||
pub cp11: f64,
|
|
||||||
pub cp12: f64,
|
|
||||||
pub cp13: f64,
|
|
||||||
pub cp14: f64,
|
|
||||||
pub cp15: f64,
|
|
||||||
pub cp16: f64,
|
|
||||||
pub cp17: f64,
|
|
||||||
pub cp18: f64,
|
|
||||||
pub cp19: f64,
|
|
||||||
pub cp20: f64,
|
|
||||||
pub cp21: f64,
|
|
||||||
pub cp22: f64,
|
|
||||||
pub cp23: f64,
|
|
||||||
pub cp24: f64,
|
|
||||||
pub cp25: f64,
|
|
||||||
pub gameid: Option<String>,
|
|
||||||
pub ddnet7: bool,
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
use mysql_common::{chrono::NaiveDateTime, frunk::HList};
|
|
||||||
use rocket::serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
pub type TeamRaceRow = HList!(String, String, f64, NaiveDateTime, String, String, bool);
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
|
||||||
#[serde(crate = "rocket::serde")]
|
|
||||||
pub struct TeamRace {
|
|
||||||
pub name: String,
|
|
||||||
pub map: String,
|
|
||||||
pub time: f64,
|
|
||||||
pub timestamp: NaiveDateTime,
|
|
||||||
pub id: String,
|
|
||||||
pub gameid: String,
|
|
||||||
pub ddnet7: bool,
|
|
||||||
}
|
|
115
src/main.rs
115
src/main.rs
|
@ -1,103 +1,28 @@
|
||||||
|
use database::DatabaseHandler;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate rocket;
|
extern crate rocket;
|
||||||
|
|
||||||
use database::create_pool;
|
|
||||||
use rocket::serde::json::Json;
|
|
||||||
use rocket::State;
|
|
||||||
|
|
||||||
extern crate dotenv;
|
extern crate dotenv;
|
||||||
use dotenv::dotenv;
|
|
||||||
|
|
||||||
mod database;
|
mod database;
|
||||||
use database::{map::Map, race::Race, DatabasePoolStore};
|
|
||||||
|
|
||||||
#[get("/maps")]
|
#[rocket::main]
|
||||||
fn get_all_maps(db_pool: &State<DatabasePoolStore>) -> Option<Json<Vec<Map>>> {
|
async fn main() {
|
||||||
match database::get_maps(&db_pool.0) {
|
let client = match DatabaseHandler::create().await {
|
||||||
Ok(maps) => Some(Json(maps)),
|
Ok(client) => client,
|
||||||
Err(err) => {
|
Err(err) => panic!(
|
||||||
println!("{err}");
|
"Encountered an error while connecting to the database!\n{}",
|
||||||
None
|
err
|
||||||
}
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
match rocket::build()
|
||||||
|
.manage(client)
|
||||||
|
.mount("/", routes![])
|
||||||
|
.launch()
|
||||||
|
.await
|
||||||
|
{
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) => println!("Encountered an error while starting rocket!\n{}", err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[get("/maps/<map>")]
|
|
||||||
fn get_map_by_name(db_pool: &State<DatabasePoolStore>, map: &str) -> Option<Json<Map>> {
|
|
||||||
match database::get_map_by_name(&db_pool.0, map) {
|
|
||||||
Ok(map) => map.map(Json),
|
|
||||||
Err(err) => {
|
|
||||||
println!("{err}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/races/by-player/<player>")]
|
|
||||||
fn get_races_by_player(
|
|
||||||
db_pool: &State<DatabasePoolStore>,
|
|
||||||
player: &str,
|
|
||||||
) -> Option<Json<Vec<Race>>> {
|
|
||||||
match database::get_races_by_player(&db_pool.0, player) {
|
|
||||||
Ok(races) => Some(Json(races)),
|
|
||||||
Err(err) => {
|
|
||||||
println!("{err}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/races/by-id/<id>")]
|
|
||||||
fn get_races_by_id(db_pool: &State<DatabasePoolStore>, id: &str) -> Option<Json<Vec<Race>>> {
|
|
||||||
match database::get_races_by_id(&db_pool.0, id) {
|
|
||||||
Ok(races) => Some(Json(races)),
|
|
||||||
Err(err) => {
|
|
||||||
println!("{err}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/races/by-map/<map>")]
|
|
||||||
fn get_races_by_map(db_pool: &State<DatabasePoolStore>, map: &str) -> Option<Json<Vec<Race>>> {
|
|
||||||
match database::get_races_by_map(&db_pool.0, map) {
|
|
||||||
Ok(races) => Some(Json(races)),
|
|
||||||
Err(err) => {
|
|
||||||
println!("{err}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[get("/player/<player>/playtime")]
|
|
||||||
fn get_player_playtime(
|
|
||||||
db_pool: &State<DatabasePoolStore>,
|
|
||||||
player: &str,
|
|
||||||
) -> Option<Json<(Option<f64>, Option<i32>)>> {
|
|
||||||
match database::get_player_total_playtime(&db_pool.0, player) {
|
|
||||||
Ok(playtime) => Some(Json(playtime)),
|
|
||||||
Err(err) => {
|
|
||||||
println!("{err}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[launch]
|
|
||||||
fn rocket() -> _ {
|
|
||||||
dotenv().ok();
|
|
||||||
|
|
||||||
let db_pool = create_pool();
|
|
||||||
|
|
||||||
rocket::build().manage(DatabasePoolStore(db_pool)).mount(
|
|
||||||
"/",
|
|
||||||
routes![
|
|
||||||
get_all_maps,
|
|
||||||
get_map_by_name,
|
|
||||||
get_races_by_player,
|
|
||||||
get_races_by_id,
|
|
||||||
get_races_by_map,
|
|
||||||
get_player_playtime
|
|
||||||
],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user