From e1b362aa1c7de2c7a7ef360a46b32a65670bc7ac Mon Sep 17 00:00:00 2001 From: Joey Hines Date: Sun, 14 Nov 2021 16:28:55 -0700 Subject: [PATCH] Implemented sorting an ordering for selling + Added parameter validation as well + Currently, Restock and Price are the two sorting methods available --- geoffrey_api/src/commands/add_item.rs | 8 +++++ geoffrey_api/src/commands/mod.rs | 9 +++++- geoffrey_api/src/commands/selling.rs | 31 +++++++++++++++++-- geoffrey_models/src/models/item.rs | 21 +++++++++++++ .../src/models/parameters/selling_params.rs | 2 +- .../src/models/response/api_error.rs | 4 +++ 6 files changed, 70 insertions(+), 5 deletions(-) diff --git a/geoffrey_api/src/commands/add_item.rs b/geoffrey_api/src/commands/add_item.rs index afe7f61..288e5eb 100644 --- a/geoffrey_api/src/commands/add_item.rs +++ b/geoffrey_api/src/commands/add_item.rs @@ -28,6 +28,14 @@ impl Command for AddItem { CommandLevel::REGISTERED } + fn validate_parameters(req: &Self::Req) -> Result<()> { + if req.quantity == 0 { + Err(GeoffreyAPIError::ParameterInvalid("quantity".to_string())) + } else { + Ok(()) + } + } + fn run_command(ctx: Arc, req: Self::Req, user: Option) -> Result { let user = user.unwrap(); diff --git a/geoffrey_api/src/commands/mod.rs b/geoffrey_api/src/commands/mod.rs index 91d72d3..ae803dd 100644 --- a/geoffrey_api/src/commands/mod.rs +++ b/geoffrey_api/src/commands/mod.rs @@ -42,6 +42,10 @@ pub trait Command { fn command_level() -> CommandLevel; fn run_command(ctx: Arc, req: Self::Req, user: Option) -> Result; + fn validate_parameters(_: &Self::Req) -> Result<()> { + Ok(()) + } + fn user_is_authorized(token: &Option, user: &Option) -> Result<()> { if let Some(token) = token { if !match Self::command_level() { @@ -77,7 +81,10 @@ pub fn handle_command(ctx: Arc, req: T::Req) -> Result T::run_command(ctx, req, user), + Ok(_) => { + T::validate_parameters(&req)?; + T::run_command(ctx, req, user) + } Err(e) => Err(e), } } diff --git a/geoffrey_api/src/commands/selling.rs b/geoffrey_api/src/commands/selling.rs index ec260c9..c68fa75 100644 --- a/geoffrey_api/src/commands/selling.rs +++ b/geoffrey_api/src/commands/selling.rs @@ -1,8 +1,9 @@ use crate::commands::{Command, RequestType}; use crate::context::Context; use crate::Result; +use geoffrey_models::models::item::ItemListing; use geoffrey_models::models::locations::{LocationDataDb, LocationDb}; -use geoffrey_models::models::parameters::selling_params::SellingParams; +use geoffrey_models::models::parameters::selling_params::{ItemSort, Order, SellingParams}; use geoffrey_models::models::player::Player; use geoffrey_models::models::response::selling_listing::SellingListing; use geoffrey_models::models::CommandLevel; @@ -27,7 +28,7 @@ impl Command for Selling { } fn run_command(ctx: Arc, req: Self::Req, _: Option) -> Result { - let shops: Vec = ctx + let mut listings: Vec = ctx .db .filter(|_, loc: &LocationDb| { if let LocationDataDb::Shop(shop_data) = loc.loc_data.clone() { @@ -68,7 +69,31 @@ impl Command for Selling { .flatten() .collect(); - Ok(shops) + let sort = req.sort.unwrap_or(ItemSort::Restock); + + match sort { + ItemSort::Price => { + listings.sort_by(|a, b| ItemListing::order_by_price(&a.listing, &b.listing)) + } + ItemSort::Restock => { + listings.sort_by(|a, b| ItemListing::order_by_restock(&a.listing, &b.listing)) + } + } + + let ordering = if let Some(order) = req.order { + order + } else { + match sort { + ItemSort::Price => Order::Low, + ItemSort::Restock => Order::High, + } + }; + + if ordering == Order::High { + listings.reverse(); + } + + Ok(listings) } } diff --git a/geoffrey_models/src/models/item.rs b/geoffrey_models/src/models/item.rs index ef9ce37..0d168ca 100644 --- a/geoffrey_models/src/models/item.rs +++ b/geoffrey_models/src/models/item.rs @@ -1,5 +1,6 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; +use std::cmp::Ordering; #[derive(Serialize, Deserialize, Debug, Clone, Hash, Eq, PartialEq)] pub struct Item { @@ -33,4 +34,24 @@ impl ItemListing { restocked_time: Utc::now(), } } + + pub fn normalized_price(&self) -> f32 { + if self.count_per_price == 0 { + f32::MAX + } else { + (self.price as f32) / (self.count_per_price as f32) + } + } + + pub fn order_by_price(a: &Self, b: &Self) -> Ordering { + // Bit of a hack so we can using integer ordering + let a_price = (a.normalized_price() * 1000.0) as u64; + let b_price = (b.normalized_price() * 1000.0) as u64; + + a_price.cmp(&b_price) + } + + pub fn order_by_restock(a: &Self, b: &Self) -> Ordering { + a.restocked_time.cmp(&b.restocked_time) + } } diff --git a/geoffrey_models/src/models/parameters/selling_params.rs b/geoffrey_models/src/models/parameters/selling_params.rs index 4e161d3..65621a1 100644 --- a/geoffrey_models/src/models/parameters/selling_params.rs +++ b/geoffrey_models/src/models/parameters/selling_params.rs @@ -8,7 +8,7 @@ pub enum ItemSort { Restock, } -#[derive(Debug, Serialize, Deserialize, Clone)] +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] pub enum Order { High, Low, diff --git a/geoffrey_models/src/models/response/api_error.rs b/geoffrey_models/src/models/response/api_error.rs index 041cd81..55c2121 100644 --- a/geoffrey_models/src/models/response/api_error.rs +++ b/geoffrey_models/src/models/response/api_error.rs @@ -10,6 +10,7 @@ pub enum GeoffreyAPIError { DatabaseError(String), TokenNotAuthorized, MultipleLocationsMatch, + ParameterInvalid(String), } impl Display for GeoffreyAPIError { @@ -30,6 +31,9 @@ impl Display for GeoffreyAPIError { GeoffreyAPIError::MultipleLocationsMatch => { "The location query returned multiple locations.".to_string() } + GeoffreyAPIError::ParameterInvalid(param) => { + format!("Parameter \"{}\" is invalid", param) + } }; write!(f, "{}", string) }