thoughts on new api structure, formats code
This commit is contained in:
@@ -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)
|
||||||
@@ -160,7 +191,12 @@ impl<'q> AsPlayer<'q> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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};
|
||||||
|
|||||||
@@ -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
|
||||||
///
|
///
|
||||||
|
|||||||
@@ -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,
|
|
||||||
);
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
@@ -81,24 +77,36 @@ mod endpoints {
|
|||||||
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)
|
||||||
})
|
})
|
||||||
@@ -107,12 +115,16 @@ mod endpoints {
|
|||||||
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| {
|
||||||
|
|||||||
Reference in New Issue
Block a user