thoughts on new api structure, formats code

This commit is contained in:
2019-10-15 14:42:57 +02:00
parent 8399ffebf7
commit 6101aaa9e9
8 changed files with 176 additions and 139 deletions

View File

@@ -4,8 +4,10 @@
//! This module wraps all needed database operations. //! This module wraps all needed database operations.
//! It exports a public API for integration with various clients (REST Api, CLI, ...) //! It exports a public API for integration with various clients (REST Api, CLI, ...)
extern crate dotenv; extern crate dotenv;
#[macro_use] extern crate diesel; #[macro_use]
#[macro_use] extern crate serde_derive; extern crate diesel;
#[macro_use]
extern crate serde_derive;
use diesel::prelude::*; use diesel::prelude::*;
use diesel::query_dsl::RunQueryDsl; use diesel::query_dsl::RunQueryDsl;
@@ -16,8 +18,8 @@ pub mod models;
mod schema; mod schema;
pub use models::{ pub use models::{
item::{Item, LootManager},
claim::{Claim, Claims}, claim::{Claim, Claims},
item::{Item, LootManager},
player::{Player, Players}, player::{Player, Players},
}; };
@@ -30,6 +32,33 @@ pub type QueryResult<T> = Result<T, diesel::result::Error>;
/// The result of an action provided by DbApi /// The result of an action provided by DbApi
pub type ActionResult<R> = Result<R, diesel::result::Error>; pub type ActionResult<R> = Result<R, diesel::result::Error>;
pub enum ApiError {
DieselError(diesel::result::Error),
InvalidAction(String),
}
pub type ApiResult<R> = Result<R, ApiError>;
pub enum ApiActions<'a> {
FetchPlayers,
FetchInventory,
// Player actions
FetchLoot(i32),
UpdateWealth(i32, f32),
BuyItems(i32, &'a Vec<(i32, Option<f32>)>),
SellItems(i32, &'a Vec<(i32, Option<f32>)>),
ClaimItem(i32, i32),
UnclaimItem(i32, i32),
// Group actions
AddLoot(&'a Vec<Item>),
}
pub enum AdminActions {
AddPlayer(String, f32),
//AddInventoryItem(pub String, pub i32),
ResolveClaims,
//SetClaimsTimeout(pub i32),
}
/// A wrapper providing an API over the database /// A wrapper providing an API over the database
/// It offers a convenient way to deal with connection. /// It offers a convenient way to deal with connection.
@@ -138,18 +167,20 @@ impl<'q> AsPlayer<'q> {
/// ///
/// # Returns /// # Returns
/// Result containing the difference in coins after operation /// Result containing the difference in coins after operation
pub fn buy<'a>(self, params: &Vec<(i32, Option<f32>)>) -> ActionResult<(Vec<models::Item>, (i32, i32, i32, i32))> { pub fn buy<'a>(
self,
params: &Vec<(i32, Option<f32>)>,
) -> ActionResult<(Vec<models::Item>, (i32, i32, i32, i32))> {
let mut cumulated_diff: Vec<(i32, i32, i32, i32)> = Vec::with_capacity(params.len()); let mut cumulated_diff: Vec<(i32, i32, i32, i32)> = Vec::with_capacity(params.len());
let mut added_items: Vec<models::Item> = Vec::with_capacity(params.len()); let mut added_items: Vec<models::Item> = Vec::with_capacity(params.len());
for (item_id, price_mod) in params.into_iter() { for (item_id, price_mod) in params.into_iter() {
if let Ok((item, diff)) = self.conn.transaction(|| { if let Ok((item, diff)) = self.conn.transaction(|| {
// Find item in inventory // Find item in inventory
let item = models::item::Inventory(self.conn).find(*item_id)?; let item = models::item::Inventory(self.conn).find(*item_id)?;
let new_item = models::item::LootManager(self.conn, self.id) let new_item = models::item::LootManager(self.conn, self.id).add_from(&item)?;
.add_from(&item)?;
let sell_price = match price_mod { let sell_price = match price_mod {
Some(modifier) => item.base_price as f32 * modifier, Some(modifier) => item.base_price as f32 * modifier,
None => item.base_price as f32 None => item.base_price as f32,
}; };
models::player::AsPlayer(self.conn, self.id) models::player::AsPlayer(self.conn, self.id)
.update_wealth(-sell_price) .update_wealth(-sell_price)
@@ -159,8 +190,13 @@ impl<'q> AsPlayer<'q> {
added_items.push(item); added_items.push(item);
} }
} }
let all_diff = cumulated_diff.into_iter().fold((0,0,0,0), |sum, diff| { let all_diff = cumulated_diff.into_iter().fold((0, 0, 0, 0), |sum, diff| {
(sum.0 + diff.0, sum.1 + diff.1, sum.2 + diff.2, sum.3 + diff.3) (
sum.0 + diff.0,
sum.1 + diff.1,
sum.2 + diff.2,
sum.3 + diff.3,
)
}); });
Ok((added_items, all_diff)) Ok((added_items, all_diff))
} }
@@ -168,10 +204,7 @@ impl<'q> AsPlayer<'q> {
/// ///
/// # Returns /// # Returns
/// Result containing the difference in coins after operation /// Result containing the difference in coins after operation
pub fn sell( pub fn sell(self, params: &Vec<(i32, Option<f32>)>) -> ActionResult<(i32, i32, i32, i32)> {
self,
params: &Vec<(i32, Option<f32>)>,
) -> ActionResult<(i32, i32, i32, i32)> {
let mut all_results: Vec<(i32, i32, i32, i32)> = Vec::with_capacity(params.len()); let mut all_results: Vec<(i32, i32, i32, i32)> = Vec::with_capacity(params.len());
for (loot_id, price_mod) in params.into_iter() { for (loot_id, price_mod) in params.into_iter() {
let res = self.conn.transaction(|| { let res = self.conn.transaction(|| {
@@ -186,13 +219,17 @@ impl<'q> AsPlayer<'q> {
all_results.push(diff.as_tuple()) all_results.push(diff.as_tuple())
} else { } else {
// TODO: need to find a better way to deal with errors // TODO: need to find a better way to deal with errors
return Err(diesel::result::Error::NotFound) return Err(diesel::result::Error::NotFound);
} }
} }
Ok(all_results.into_iter().fold((0,0,0,0), |sum, diff| { Ok(all_results.into_iter().fold((0, 0, 0, 0), |sum, diff| {
(sum.0 + diff.0, sum.1 + diff.1, sum.2 + diff.2, sum.3 + diff.3) (
sum.0 + diff.0,
sum.1 + diff.1,
sum.2 + diff.2,
sum.3 + diff.3,
)
})) }))
} }
/// Adds the value in gold to the player's wealth. /// Adds the value in gold to the player's wealth.
@@ -202,7 +239,6 @@ impl<'q> AsPlayer<'q> {
models::player::AsPlayer(self.conn, self.id) models::player::AsPlayer(self.conn, self.id)
.update_wealth(value_in_gp) .update_wealth(value_in_gp)
.map(|w| w.as_tuple()) .map(|w| w.as_tuple())
} }
/// Put a claim on a specific item /// Put a claim on a specific item
pub fn claim(self, item: i32) -> ActionResult<()> { pub fn claim(self, item: i32) -> ActionResult<()> {
@@ -232,8 +268,7 @@ impl<'q> AsAdmin<'q> {
/// ///
/// Takes the player name and starting wealth (in gold value). /// Takes the player name and starting wealth (in gold value).
pub fn add_player(self, name: &str, start_wealth: f32) -> ActionResult<()> { pub fn add_player(self, name: &str, start_wealth: f32) -> ActionResult<()> {
models::player::Players(self.0) models::player::Players(self.0).add(name, start_wealth)?;
.add(name, start_wealth)?;
Ok(()) Ok(())
} }
@@ -260,10 +295,9 @@ impl<'q> AsAdmin<'q> {
self.0.transaction(|| { self.0.transaction(|| {
claim.resolve_claim(self.0)?; claim.resolve_claim(self.0)?;
//models::item::LootManager(self.0, 0).set_owner(claim.loot_id, claim.player_id)?; //models::item::LootManager(self.0, 0).set_owner(claim.loot_id, claim.player_id)?;
models::player::AsPlayer(self.0, player_id) models::player::AsPlayer(self.0, player_id).update_debt(item.sell_value())
.update_debt(item.sell_value())
})?; })?;
}, }
_ => (), _ => (),
} }
} }
@@ -355,13 +389,22 @@ mod tests_old {
assert_eq!(claims.len(), 0); assert_eq!(claims.len(), 0);
// Add items // Add items
assert_eq!(DbApi::with_conn(&conn).as_admin().add_loot(vec![ assert_eq!(
("Épée", 40), DbApi::with_conn(&conn)
("Arc", 40), .as_admin()
]).is_ok(), true); .add_loot(vec![("Épée", 40), ("Arc", 40),])
.is_ok(),
true
);
// Add players // Add players
DbApi::with_conn(&conn).as_admin().add_player("Player1", 0.0).unwrap(); DbApi::with_conn(&conn)
DbApi::with_conn(&conn).as_admin().add_player("Player2", 0.0).unwrap(); .as_admin()
.add_player("Player1", 0.0)
.unwrap();
DbApi::with_conn(&conn)
.as_admin()
.add_player("Player2", 0.0)
.unwrap();
// Put claims on one different item each // Put claims on one different item each
DbApi::with_conn(&conn).as_player(1).claim(1).unwrap(); DbApi::with_conn(&conn).as_player(1).claim(1).unwrap();
DbApi::with_conn(&conn).as_player(2).claim(2).unwrap(); DbApi::with_conn(&conn).as_player(2).claim(2).unwrap();
@@ -370,7 +413,10 @@ mod tests_old {
// Check that both players received an item // Check that both players received an item
let players = DbApi::with_conn(&conn).fetch_players().unwrap(); let players = DbApi::with_conn(&conn).fetch_players().unwrap();
for &i in [1, 2].into_iter() { for &i in [1, 2].into_iter() {
assert_eq!(DbApi::with_conn(&conn).as_player(i).loot().unwrap().len(), 1); assert_eq!(
DbApi::with_conn(&conn).as_player(i).loot().unwrap().len(),
1
);
let player = players.get(i as usize).unwrap(); let player = players.get(i as usize).unwrap();
assert_eq!(player.debt, 20); assert_eq!(player.debt, 20);
} }
@@ -448,9 +494,7 @@ mod tests_old {
.add_player("Player", 1000.0) .add_player("Player", 1000.0)
.unwrap(); .unwrap();
// Buy an item // Buy an item
let bought = DbApi::with_conn(&conn) let bought = DbApi::with_conn(&conn).as_player(1).buy(&vec![(1, None)]);
.as_player(1)
.buy(&vec![(1, None)]);
assert_eq!(bought.ok(), Some((0, 0, 0, -8))); // Returns diff of player wealth ? assert_eq!(bought.ok(), Some((0, 0, 0, -8))); // Returns diff of player wealth ?
let chest = DbApi::with_conn(&conn).as_player(1).loot().unwrap(); let chest = DbApi::with_conn(&conn).as_player(1).loot().unwrap();
assert_eq!(chest.len(), 1); assert_eq!(chest.len(), 1);
@@ -461,10 +505,14 @@ mod tests_old {
let player = players.get(1).unwrap(); let player = players.get(1).unwrap();
assert_eq!(player.pp, 2); assert_eq!(player.pp, 2);
// A player cannot sell loot from an other's chest // A player cannot sell loot from an other's chest
let result = DbApi::with_conn(&conn).as_player(0).sell(&vec![(loot.id, None)]); let result = DbApi::with_conn(&conn)
.as_player(0)
.sell(&vec![(loot.id, None)]);
assert_eq!(result.is_ok(), false); assert_eq!(result.is_ok(), false);
// Sell back // Sell back
let sold = DbApi::with_conn(&conn).as_player(1).sell(&vec![(loot.id, None)]); let sold = DbApi::with_conn(&conn)
.as_player(1)
.sell(&vec![(loot.id, None)]);
assert_eq!(sold.ok(), Some((0, 0, 0, 4))); assert_eq!(sold.ok(), Some((0, 0, 0, 4)));
let chest = DbApi::with_conn(&conn).as_player(1).loot().unwrap(); let chest = DbApi::with_conn(&conn).as_player(1).loot().unwrap();
assert_eq!(chest.len(), 0); assert_eq!(chest.len(), 0);

View File

@@ -1,5 +1,5 @@
use diesel::prelude::*;
use crate::{DbConnection, QueryResult}; use crate::{DbConnection, QueryResult};
use diesel::prelude::*;
use crate::models::{self, item::Loot}; use crate::models::{self, item::Loot};
use crate::schema::claims; use crate::schema::claims;
@@ -27,8 +27,7 @@ impl Claim {
} }
fn remove(&self, conn: &DbConnection) -> QueryResult<()> { fn remove(&self, conn: &DbConnection) -> QueryResult<()> {
diesel::delete(claims::table.find(self.id)) diesel::delete(claims::table.find(self.id)).execute(conn)?;
.execute(conn)?;
Ok(()) Ok(())
} }
} }
@@ -36,10 +35,8 @@ impl Claim {
pub struct Claims<'q>(pub &'q DbConnection); pub struct Claims<'q>(pub &'q DbConnection);
impl<'q> Claims<'q> { impl<'q> Claims<'q> {
pub fn all(&self) -> QueryResult<Vec<Claim>> { pub fn all(&self) -> QueryResult<Vec<Claim>> {
claims::table claims::table.load(self.0)
.load(self.0)
} }
/// Finds a single claim by association of player and loot ids. /// Finds a single claim by association of player and loot ids.
@@ -85,15 +82,12 @@ impl<'q> Claims<'q> {
pub(crate) fn grouped_by_item(&self) -> QueryResult<Vec<(models::item::Item, Vec<Claim>)>> { pub(crate) fn grouped_by_item(&self) -> QueryResult<Vec<(models::item::Item, Vec<Claim>)>> {
let group_loot: Vec<Loot> = Loot::owned_by(0).load(self.0)?; let group_loot: Vec<Loot> = Loot::owned_by(0).load(self.0)?;
let claims = claims::table let claims = claims::table.load(self.0)?.grouped_by(&group_loot);
.load(self.0)? Ok(group_loot
.grouped_by(&group_loot); .into_iter()
Ok(
group_loot.into_iter()
.map(|loot| loot.into_item()) .map(|loot| loot.into_item())
.zip(claims) .zip(claims)
.collect::<Vec<_>>() .collect::<Vec<_>>())
)
} }
} }
@@ -117,17 +111,23 @@ mod tests {
type TestResult = Result<(), diesel::result::Error>; type TestResult = Result<(), diesel::result::Error>;
fn test_connection() -> Result<DbConnection, diesel::result::Error> { fn test_connection() -> Result<DbConnection, diesel::result::Error> {
let conn = DbConnection::establish(":memory:") let conn =
.map_err(|_| diesel::result::Error::NotFound)?; DbConnection::establish(":memory:").map_err(|_| diesel::result::Error::NotFound)?;
diesel_migrations::run_pending_migrations(&conn) diesel_migrations::run_pending_migrations(&conn)
.map_err(|_| diesel::result::Error::NotFound)?; .map_err(|_| diesel::result::Error::NotFound)?;
let manager = models::player::Players(&conn); let manager = models::player::Players(&conn);
manager.add("Player1", 0.0)?; manager.add("Player1", 0.0)?;
manager.add("Player2", 0.0)?; manager.add("Player2", 0.0)?;
crate::LootManager(&conn, 0) crate::LootManager(&conn, 0).add_from(&crate::Item {
.add_from(&crate::Item{ id: 0, name: "Epee".to_string(), base_price: 30 })?; id: 0,
crate::LootManager(&conn, 1) name: "Epee".to_string(),
.add_from(&crate::Item{ id: 0, name: "Arc".to_string(), base_price: 20 })?; base_price: 30,
})?;
crate::LootManager(&conn, 1).add_from(&crate::Item {
id: 0,
name: "Arc".to_string(),
base_price: 20,
})?;
Ok(conn) Ok(conn)
} }

View File

@@ -1,11 +1,9 @@
use diesel::dsl::{exists, Eq, Filter, Find, Select}; use diesel::dsl::{exists, Eq, Filter, Find, Select};
use diesel::expression::exists::Exists; use diesel::expression::exists::Exists;
use diesel::prelude::*; use diesel::prelude::*;
use crate::{DbConnection, QueryResult};
use crate::schema::{items, looted}; use crate::schema::{items, looted};
use crate::{DbConnection, QueryResult};
type ItemColumns = (looted::id, looted::name, looted::base_price); type ItemColumns = (looted::id, looted::name, looted::base_price);
const ITEM_COLUMNS: ItemColumns = (looted::id, looted::name, looted::base_price); const ITEM_COLUMNS: ItemColumns = (looted::id, looted::name, looted::base_price);
type OwnedBy = Select<OwnedLoot, ItemColumns>; type OwnedBy = Select<OwnedLoot, ItemColumns>;
@@ -35,23 +33,18 @@ impl Item {
pub struct Inventory<'q>(pub &'q DbConnection); pub struct Inventory<'q>(pub &'q DbConnection);
impl<'q> Inventory<'q> { impl<'q> Inventory<'q> {
pub fn all(&self) -> QueryResult<Vec<Item>> { pub fn all(&self) -> QueryResult<Vec<Item>> {
items::table.load::<Item>(self.0) items::table.load::<Item>(self.0)
} }
pub fn find(&self, item_id: i32) -> QueryResult<Item> { pub fn find(&self, item_id: i32) -> QueryResult<Item> {
items::table items::table.find(item_id).first::<Item>(self.0)
.find(item_id)
.first::<Item>(self.0)
} }
} }
type WithOwner = Eq<looted::owner_id, i32>; type WithOwner = Eq<looted::owner_id, i32>;
type OwnedLoot = Filter<looted::table, WithOwner>; type OwnedLoot = Filter<looted::table, WithOwner>;
/// Represents an item that has been looted, /// Represents an item that has been looted,
/// hence has an owner. /// hence has an owner.
#[derive(Identifiable, Debug, Queryable, Serialize)] #[derive(Identifiable, Debug, Queryable, Serialize)]
@@ -89,10 +82,8 @@ impl Loot {
} }
pub(super) fn find(id: i32) -> Find<looted::table, i32> { pub(super) fn find(id: i32) -> Find<looted::table, i32> {
looted::table looted::table.find(id)
.find(id)
} }
} }
/// Manager for a player's loot /// Manager for a player's loot
@@ -106,28 +97,24 @@ impl<'q> LootManager<'q> {
/// Finds an item by id /// Finds an item by id
pub fn find(&self, loot_id: i32) -> QueryResult<Item> { pub fn find(&self, loot_id: i32) -> QueryResult<Item> {
Ok( Ok(Loot::find(loot_id).first(self.0).and_then(|loot: Loot| {
Loot::find(loot_id)
.first(self.0)
.and_then(|loot: Loot| {
if loot.owner != self.1 { if loot.owner != self.1 {
Err(diesel::result::Error::NotFound) Err(diesel::result::Error::NotFound)
} else { } else {
Ok( Item { id: loot.id, name: loot.name, base_price: loot.base_price } ) Ok(Item {
id: loot.id,
name: loot.name,
base_price: loot.base_price,
})
} }
})? })?)
)
} }
/// The last item added to the chest /// The last item added to the chest
pub fn last(&self) -> QueryResult<Item> { pub fn last(&self) -> QueryResult<Item> {
Ok( Ok(Item::owned_by(self.1)
Item::owned_by(self.1)
.order(looted::dsl::id.desc()) .order(looted::dsl::id.desc())
.first(self.0)? .first(self.0)?)
)
} }
/// Adds a copy of the given item inside player chest /// Adds a copy of the given item inside player chest
@@ -148,8 +135,6 @@ impl<'q> LootManager<'q> {
diesel::delete(looted::table.find(deleted.id)).execute(self.0)?; diesel::delete(looted::table.find(deleted.id)).execute(self.0)?;
Ok(deleted) Ok(deleted)
} }
} }
/// An item being looted or bought. /// An item being looted or bought.

View File

@@ -3,5 +3,5 @@ pub mod item;
pub mod player; pub mod player;
pub use claim::Claim; pub use claim::Claim;
pub use item::{Item}; pub use item::Item;
pub use player::{Player, Wealth}; pub use player::{Player, Wealth};

View File

@@ -1,6 +1,6 @@
use diesel::prelude::*;
use crate::{DbConnection, QueryResult};
use crate::schema::players; use crate::schema::players;
use crate::{DbConnection, QueryResult};
use diesel::prelude::*;
/// Representation of a player in database /// Representation of a player in database
#[derive(Debug, Queryable, Serialize)] #[derive(Debug, Queryable, Serialize)]
@@ -25,19 +25,15 @@ pub struct Player {
pub struct Players<'q>(pub &'q DbConnection); pub struct Players<'q>(pub &'q DbConnection);
impl<'q> Players<'q> { impl<'q> Players<'q> {
pub fn all(&self) -> QueryResult<Vec<Player>> { pub fn all(&self) -> QueryResult<Vec<Player>> {
players::table players::table.load(self.0)
.load(self.0)
} }
pub fn add(&self, name: &str, wealth: f32) -> QueryResult<Player> { pub fn add(&self, name: &str, wealth: f32) -> QueryResult<Player> {
diesel::insert_into(players::table) diesel::insert_into(players::table)
.values(&NewPlayer::create(name, wealth)) .values(&NewPlayer::create(name, wealth))
.execute(self.0)?; .execute(self.0)?;
players::table players::table.order(players::dsl::id.desc()).first(self.0)
.order(players::dsl::id.desc())
.first(self.0)
} }
} }
@@ -67,15 +63,12 @@ impl<'q> AsPlayer<'q> {
pub fn update_debt(&self, value_in_gp: i32) -> QueryResult<()> { pub fn update_debt(&self, value_in_gp: i32) -> QueryResult<()> {
diesel::update(players::table.find(self.1)) diesel::update(players::table.find(self.1))
.set(players::dsl::debt.eq( .set(players::dsl::debt.eq(players::dsl::debt + value_in_gp))
players::dsl::debt + value_in_gp
))
.execute(self.0)?; .execute(self.0)?;
Ok(()) Ok(())
} }
} }
/// Unpack a floating value of gold pieces to integer /// Unpack a floating value of gold pieces to integer
/// values of copper, silver, gold and platinum pieces /// values of copper, silver, gold and platinum pieces
/// ///

View File

@@ -40,9 +40,4 @@ joinable!(claims -> looted (loot_id));
joinable!(claims -> players (player_id)); joinable!(claims -> players (player_id));
joinable!(looted -> players (owner_id)); joinable!(looted -> players (owner_id));
allow_tables_to_appear_in_same_query!( allow_tables_to_appear_in_same_query!(claims, items, looted, players,);
claims,
items,
looted,
players,
);

View File

@@ -3,8 +3,8 @@
//! //!
//! Contains semantic mutations of database //! Contains semantic mutations of database
//! //!
use crate::DbConnection;
use crate::models::player::Wealth; use crate::models::player::Wealth;
use crate::DbConnection;
type PlayerId = i32; type PlayerId = i32;
type ItemId = i32; type ItemId = i32;
@@ -16,7 +16,11 @@ enum LootUpdate {
} }
impl LootUpdate { impl LootUpdate {
fn add_loot(conn: &DbConnection, to_player: PlayerId, item_desc: Item) -> Result<Self, diesel::result::Error> { fn add_loot(
conn: &DbConnection,
to_player: PlayerId,
item_desc: Item,
) -> Result<Self, diesel::result::Error> {
use schema::looted::dsl::*; use schema::looted::dsl::*;
let new_item = models::item::NewLoot::to_player(to_player, &item_desc); let new_item = models::item::NewLoot::to_player(to_player, &item_desc);
diesel::insert_into(looted) diesel::insert_into(looted)
@@ -41,10 +45,10 @@ impl LootUpdate {
match self { match self {
LootUpdate::AddedItem(item_id) => { LootUpdate::AddedItem(item_id) => {
// Remove the item // Remove the item
}, }
LootUpdate::RemovedItem(item) => { LootUpdate::RemovedItem(item) => {
// Add the item back // Add the item back
}, }
LootUpdate::GivenToPlayer(item_id) => { LootUpdate::GivenToPlayer(item_id) => {
// Change owner to group // Change owner to group
} }

View File

@@ -2,8 +2,8 @@ use actix_cors::Cors;
use actix_files as fs; use actix_files as fs;
use actix_web::{web, App, Error, HttpResponse, HttpServer}; use actix_web::{web, App, Error, HttpResponse, HttpServer};
use futures::Future; use futures::Future;
use lootalot_db::models::Item;
use lootalot_db::{DbApi, Pool, QueryResult}; use lootalot_db::{DbApi, Pool, QueryResult};
use lootalot_db::{Item, LootManager};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::env; use std::env;
@@ -33,14 +33,10 @@ type AppPool = web::Data<Pool>;
pub fn db_call<J, Q>(pool: AppPool, query: Q) -> impl Future<Item = HttpResponse, Error = Error> pub fn db_call<J, Q>(pool: AppPool, query: Q) -> impl Future<Item = HttpResponse, Error = Error>
where where
J: serde::ser::Serialize + Send + 'static, J: serde::ser::Serialize + Send + 'static,
Q: Fn(DbApi) -> QueryResult<J> + Send + 'static, Q: Fn(DbConnection) -> QueryResult<J> + Send + 'static,
{ {
let conn = pool.get().unwrap(); let conn = pool.get().unwrap();
web::block(move || { web::block(move || query(conn)).then(|res| match res {
let api = DbApi::with_conn(&conn);
query(api)
})
.then(|res| match res {
Ok(r) => HttpResponse::Ok().json(r), Ok(r) => HttpResponse::Ok().json(r),
Err(e) => { Err(e) => {
dbg!(&e); dbg!(&e);
@@ -77,42 +73,58 @@ mod endpoints {
items: Vec<(i32, Option<f32>)>, items: Vec<(i32, Option<f32>)>,
} }
pub fn players_list(pool: AppPool) -> impl Future<Item = HttpResponse, Error = Error>{ pub fn players_list(pool: AppPool) -> impl Future<Item = HttpResponse, Error = Error> {
db_call(pool, move |api| api.fetch_players()) db_call(pool, move |api| api.fetch_players())
} }
pub fn player_loot(pool: AppPool, player_id: web::Path<i32>) -> impl Future<Item = HttpResponse, Error = Error>{ pub fn player_loot(
db_call(pool, move |api| api.as_player(*player_id).loot()) pool: AppPool,
player_id: web::Path<i32>,
) -> impl Future<Item = HttpResponse, Error = Error> {
db_call(pool, move |conn| LootManager(&conn, *player_id).all())
} }
pub fn update_wealth(pool: AppPool, data: web::Json<WealthUpdate>) -> impl Future<Item = HttpResponse, Error = Error>{ pub fn update_wealth(
pool: AppPool,
data: web::Json<WealthUpdate>,
) -> impl Future<Item = HttpResponse, Error = Error> {
db_call(pool, move |api| { db_call(pool, move |api| {
api.as_player(data.player_id) api.as_player(data.player_id)
.update_wealth(data.value_in_gp) .update_wealth(data.value_in_gp)
}) })
} }
pub fn buy_item(pool: AppPool, data: web::Json<LootUpdate>) -> impl Future<Item = HttpResponse, Error = Error>{ pub fn buy_item(
pool: AppPool,
data: web::Json<LootUpdate>,
) -> impl Future<Item = HttpResponse, Error = Error> {
db_call(pool, move |api| { db_call(pool, move |api| {
api.as_player(data.player_id).buy(&data.items) api.as_player(data.player_id).buy(&data.items)
}) })
} }
pub fn sell_item(pool: AppPool, data: web::Json<LootUpdate>) -> impl Future<Item = HttpResponse, Error = Error>{ pub fn sell_item(
pool: AppPool,
data: web::Json<LootUpdate>,
) -> impl Future<Item = HttpResponse, Error = Error> {
db_call(pool, move |api| { db_call(pool, move |api| {
api.as_player(data.player_id).sell(&data.items) api.as_player(data.player_id).sell(&data.items)
}) })
} }
pub fn player_claims(pool: AppPool) -> impl Future<Item = HttpResponse, Error = Error>{ pub fn player_claims(pool: AppPool) -> impl Future<Item = HttpResponse, Error = Error> {
db_call(pool, move |api| api.fetch_claims()) db_call(pool, move |api| api.fetch_claims())
} }
pub fn put_claim(pool: AppPool, (player, loot): (web::Path<i32>, web::Json<PlayerClaim>)) -> impl Future<Item = HttpResponse, Error = Error>{ pub fn put_claim(
db_call(pool, move |api| { pool: AppPool,
api.as_player(*player).claim(loot.item_id) (player, loot): (web::Path<i32>, web::Json<PlayerClaim>),
}) ) -> impl Future<Item = HttpResponse, Error = Error> {
db_call(pool, move |api| api.as_player(*player).claim(loot.item_id))
} }
pub fn delete_claim(pool: AppPool, (player, data): (web::Path<i32>, web::Json<PlayerClaim>)) -> impl Future<Item = HttpResponse, Error = Error>{ pub fn delete_claim(
pool: AppPool,
(player, data): (web::Path<i32>, web::Json<PlayerClaim>),
) -> impl Future<Item = HttpResponse, Error = Error> {
db_call(pool, move |api| { db_call(pool, move |api| {
api.as_player(*player).unclaim(data.item_id) api.as_player(*player).unclaim(data.item_id)
}) })
@@ -137,7 +149,10 @@ pub(crate) fn serve() -> std::io::Result<()> {
web::scope("/api") web::scope("/api")
.service( .service(
web::scope("/players") web::scope("/players")
.service( web::resource("/").route(web::get().to_async(endpoints::players_list))) // List of players .service(
web::resource("/")
.route(web::get().to_async(endpoints::players_list)),
) // List of players
//.route(web::put().to_async(endpoints::new_player)) // Create/Update player //.route(web::put().to_async(endpoints::new_player)) // Create/Update player
.service( .service(
web::scope("/{player_id}") web::scope("/{player_id}")
@@ -161,10 +176,7 @@ pub(crate) fn serve() -> std::io::Result<()> {
), ),
), ),
) )
.route( .route("/claims", web::get().to_async(endpoints::player_claims))
"/claims",
web::get().to_async(endpoints::player_claims)
)
.route( .route(
"/items", "/items",
web::get().to_async(move |pool: AppPool| { web::get().to_async(move |pool: AppPool| {