Added 'remove_item' command
continuous-integration/woodpecker the build was successful Details

+ Allows a user to remove an item from a shop
+ Clippy + fmt
main
Joey Hines 2021-12-19 14:55:59 -07:00
parent 18a4373313
commit d9a3a05067
No known key found for this signature in database
GPG Key ID: 80F567B5C968F91B
11 changed files with 192 additions and 7 deletions

View File

@ -5,6 +5,7 @@ use crate::commands::edit::Edit;
use crate::commands::find::FindCommand; use crate::commands::find::FindCommand;
use crate::commands::link::LinkCommand; use crate::commands::link::LinkCommand;
use crate::commands::register::Register; use crate::commands::register::Register;
use crate::commands::remove_item::RemoveItem;
use crate::commands::selling::Selling; use crate::commands::selling::Selling;
use crate::commands::set_portal::SetPortal; use crate::commands::set_portal::SetPortal;
use crate::context::Context; use crate::context::Context;
@ -31,6 +32,7 @@ pub mod edit;
pub mod find; pub mod find;
pub mod link; pub mod link;
pub mod register; pub mod register;
pub mod remove_item;
pub mod selling; pub mod selling;
pub mod set_portal; pub mod set_portal;
@ -137,6 +139,7 @@ pub fn command_filter(
.or(create_command_filter::<Delete>(ctx.clone())) .or(create_command_filter::<Delete>(ctx.clone()))
.or(create_command_filter::<LinkCommand>(ctx.clone())) .or(create_command_filter::<LinkCommand>(ctx.clone()))
.or(create_command_filter::<Edit>(ctx.clone())) .or(create_command_filter::<Edit>(ctx.clone()))
.or(create_command_filter::<RemoveItem>(ctx.clone()))
.or(create_command_filter::<SetPortal>(ctx)), .or(create_command_filter::<SetPortal>(ctx)),
) )
} }

View File

@ -0,0 +1,52 @@
use crate::commands::{Command, RequestType};
use crate::context::Context;
use crate::Result;
use geoffrey_db::helper::{find_location_by_name_type, load_location};
use geoffrey_models::models::locations::{Location, LocationDataDb, LocationType};
use geoffrey_models::models::parameters::remove_item_params::RemoveItemParameters;
use geoffrey_models::models::player::Player;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use geoffrey_models::models::CommandLevel;
use std::sync::Arc;
pub struct RemoveItem {}
impl Command for RemoveItem {
type Req = RemoveItemParameters;
type Resp = Location;
fn command_name() -> String {
"remove_item".to_string()
}
fn request_type() -> RequestType {
RequestType::POST
}
fn command_level() -> CommandLevel {
CommandLevel::REGISTERED
}
fn run_command(ctx: Arc<Context>, req: &Self::Req, user: Option<Player>) -> Result<Self::Resp> {
let user = user.unwrap();
let mut shop = find_location_by_name_type(
&ctx.db,
&req.shop_name,
user.id.unwrap(),
LocationType::Shop,
)?;
if let LocationDataDb::Shop(shop_data) = &mut shop.loc_data {
shop_data
.item_listings
.retain(|item| item.item.name.to_lowercase() != req.item_name.to_lowercase());
let shop = ctx.db.insert(shop)?;
load_location(&ctx.db, &shop).map_err(|err| err.into())
} else {
Err(GeoffreyAPIError::EntryNotFound)
}
}
}

View File

@ -9,7 +9,7 @@ use geoffrey_models::models::parameters::add_item_params::AddItemParams;
use crate::bot::arg_parse::{option_to_i64, option_to_string}; use crate::bot::arg_parse::{option_to_i64, option_to_string};
use crate::bot::commands::{BotCommand, CommandError}; use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::formatters::display_loc; use crate::bot::formatters::display_loc_full;
use crate::bot::lang::{PLAYER_ALREADY_SELLS_ITEM, PLAYER_DOES_NOT_HAVE_MATCHING_SHOP}; use crate::bot::lang::{PLAYER_ALREADY_SELLS_ITEM, PLAYER_DOES_NOT_HAVE_MATCHING_SHOP};
use geoffrey_models::models::response::api_error::GeoffreyAPIError; use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use serenity::builder::CreateApplicationCommand; use serenity::builder::CreateApplicationCommand;
@ -94,7 +94,7 @@ impl BotCommand for AddItemCommand {
format!( format!(
"**{}** has been updated:\n{}", "**{}** has been updated:\n{}",
resp.name, resp.name,
display_loc(&resp) display_loc_full(&resp)
) )
} }
} }

View File

@ -12,7 +12,7 @@ use crate::bot::arg_parse::{
add_z_position_argument, option_to_dim, option_to_i64, option_to_loc_type, option_to_string, add_z_position_argument, option_to_dim, option_to_i64, option_to_loc_type, option_to_string,
}; };
use crate::bot::commands::{BotCommand, CommandError}; use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::formatters::display_loc; use crate::bot::formatters::display_loc_full;
use serenity::builder::CreateApplicationCommand; use serenity::builder::CreateApplicationCommand;
pub struct AddLocationCommand; pub struct AddLocationCommand;
@ -80,7 +80,7 @@ impl BotCommand for AddLocationCommand {
format!( format!(
"**{}** has been added to Geoffrey:\n{}", "**{}** has been added to Geoffrey:\n{}",
resp.name, resp.name,
display_loc(&resp) display_loc_full(&resp)
) )
} }
} }

View File

@ -24,6 +24,7 @@ pub mod edit_name;
pub mod edit_pos; pub mod edit_pos;
pub mod find; pub mod find;
pub mod register; pub mod register;
pub mod remove_item;
pub mod selling; pub mod selling;
pub mod set_portal; pub mod set_portal;

View File

@ -0,0 +1,79 @@
use async_trait::async_trait;
use reqwest::Method;
use serenity::model::interactions::application_command::{
ApplicationCommandInteraction, ApplicationCommandOptionType,
};
use geoffrey_models::models::locations::Location;
use geoffrey_models::models::parameters::remove_item_params::RemoveItemParameters;
use crate::bot::arg_parse::option_to_string;
use crate::bot::commands::{BotCommand, CommandError};
use crate::bot::formatters::display_loc_full;
use crate::bot::lang::PLAYER_DOES_NOT_HAVE_MATCHING_SHOP;
use geoffrey_models::models::response::api_error::GeoffreyAPIError;
use serenity::builder::CreateApplicationCommand;
pub struct RemoveItemCommand;
#[async_trait]
impl BotCommand for RemoveItemCommand {
type ApiParams = RemoveItemParameters;
type ApiResp = Location;
fn command_name() -> String {
"remove_item".to_string()
}
fn request_type() -> Method {
Method::POST
}
fn custom_err_resp(e: &CommandError) -> Option<String> {
match e {
CommandError::GeoffreyApi(GeoffreyAPIError::EntryNotFound) => {
Some(PLAYER_DOES_NOT_HAVE_MATCHING_SHOP.to_string())
}
_ => None,
}
}
fn create_app_command(command: &mut CreateApplicationCommand) -> &mut CreateApplicationCommand {
command
.name(Self::command_name())
.description("Remove an item from a shop")
.create_option(|option| {
option
.name("shop_name")
.description("Shop to list the item at")
.kind(ApplicationCommandOptionType::String)
.required(true)
})
.create_option(|option| {
option
.name("item_name")
.description("Name of the item to remove")
.kind(ApplicationCommandOptionType::String)
.required(true)
})
}
async fn process_arguments(
command_interaction: ApplicationCommandInteraction,
) -> Result<Self::ApiParams, CommandError> {
let options = command_interaction.data.options;
Ok(Self::ApiParams::new(
option_to_string(options.get(0), "shop_name")?,
option_to_string(options.get(1), "item_name")?,
))
}
fn build_response(resp: Self::ApiResp) -> String {
format!(
"**{}** has been updated:\n{}",
resp.name,
display_loc_full(&resp)
)
}
}

View File

@ -79,10 +79,10 @@ impl BotCommand for SellingCommand {
for item in resp { for item in resp {
writeln!( writeln!(
resp_str, resp_str,
"**{}**, {}D for {}: {} {}", "**{}**, {} for {}D: {} {}",
item.listing.item.name, item.listing.item.name,
item.listing.count_per_price, item.listing.count_per_price,
item.listing.count_per_price, item.listing.price,
item.shop_name, item.shop_name,
item.shop_loc item.shop_loc
) )

View File

@ -1,4 +1,4 @@
use geoffrey_models::models::locations::Location; use geoffrey_models::models::locations::{Location, LocationData};
use geoffrey_models::models::player::Player; use geoffrey_models::models::player::Player;
use geoffrey_models::models::Portal; use geoffrey_models::models::Portal;
@ -53,3 +53,30 @@ pub fn display_loc(loc: &Location) -> String {
display_owners(&loc.owners, 3), display_owners(&loc.owners, 3),
) )
} }
pub fn display_loc_full(loc: &Location) -> String {
let info = match &loc.loc_data {
LocationData::Shop(shop) => {
if !shop.item_listings.is_empty() {
format!(
"\n**Inventory**:\n{}",
shop.item_listings
.iter()
.map(|item| {
format!(
"**{}**, {} for {}D",
item.item.name, item.count_per_price, item.price
)
})
.collect::<Vec<String>>()
.join("\n")
)
} else {
"".to_string()
}
}
_ => "".to_string(),
};
format!("{}\n{}", display_loc(loc), info)
}

View File

@ -5,6 +5,7 @@ use crate::bot::commands::delete::DeleteCommand;
use crate::bot::commands::edit_name::EditNameCommand; use crate::bot::commands::edit_name::EditNameCommand;
use crate::bot::commands::edit_pos::EditPosCommand; use crate::bot::commands::edit_pos::EditPosCommand;
use crate::bot::commands::register::RegisterCommand; use crate::bot::commands::register::RegisterCommand;
use crate::bot::commands::remove_item::RemoveItemCommand;
use crate::bot::commands::GeoffreyCommandFn; use crate::bot::commands::GeoffreyCommandFn;
use crate::context::GeoffreyContext; use crate::context::GeoffreyContext;
use commands::add_item::AddItemCommand; use commands::add_item::AddItemCommand;
@ -95,6 +96,8 @@ pub async fn build_commands(
.add_command::<EditPosCommand>(ctx) .add_command::<EditPosCommand>(ctx)
.await? .await?
.add_command::<EditNameCommand>(ctx) .add_command::<EditNameCommand>(ctx)
.await?
.add_command::<RemoveItemCommand>(ctx)
.await?; .await?;
Ok(()) Ok(())

View File

@ -6,6 +6,7 @@ pub mod edit_params;
pub mod find_params; pub mod find_params;
pub mod link_params; pub mod link_params;
pub mod register_params; pub mod register_params;
pub mod remove_item_params;
pub mod selling_params; pub mod selling_params;
pub mod set_portal_params; pub mod set_portal_params;

View File

@ -0,0 +1,19 @@
use crate::models::parameters::GeoffreyParam;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct RemoveItemParameters {
pub shop_name: String,
pub item_name: String,
}
impl RemoveItemParameters {
pub fn new(shop_name: String, item_name: String) -> Self {
Self {
shop_name,
item_name,
}
}
}
impl GeoffreyParam for RemoveItemParameters {}