From 4dc1a9ff991ac9f7e434392ee6295ede66b40329 Mon Sep 17 00:00:00 2001 From: rakuja Date: Sat, 30 Nov 2024 23:39:15 +0100 Subject: [PATCH 01/18] chore: update libraries, bump MSRV, panic if update_cr_core is used more than once --- Cargo.toml | 7 ++++--- src/db/cr_core_initializer.rs | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index a1b9091..d3bbd44 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ authors = ["RakuJa"] # Compiler info edition = "2021" -rust-version = "1.75.0" +rust-version = "1.79.0" description = "Beyond Your Bestiary Explorer (BYBE) is a web service that provides tools to help Pathfinder 2e Game Masters." readme = "README.md" @@ -39,8 +39,8 @@ sqlx = { version = "0.8.2", features = ["runtime-async-std", "sqlite"] } cached = { version = "0.54.0", features = ["async"] } anyhow = "1.0.93" -serde = { version = "1.0.214", features = ["derive"] } -serde_json = "1.0.132" +serde = { version = "1.0.215", features = ["derive"] } +serde_json = "1.0.133" strum = {version="0.26.3", features = ["derive"]} fastrand = "2.2.0" counter = "0.6.0" @@ -56,6 +56,7 @@ dotenvy = "0.15.7" env_logger = "0.11.5" log = "0.4.22" once_cell = "1.20.2" +once = "0.3.4" [build-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread", "rt"] } diff --git a/src/db/cr_core_initializer.rs b/src/db/cr_core_initializer.rs index 4076d8e..a4c5c60 100644 --- a/src/db/cr_core_initializer.rs +++ b/src/db/cr_core_initializer.rs @@ -9,12 +9,14 @@ use crate::models::creature::creature_metadata::type_enum::CreatureTypeEnum; use crate::models::shared::rarity_enum::RarityEnum; use crate::models::shared::size_enum::SizeEnum; use anyhow::{bail, Result}; +use once::assert_has_not_been_called; use serde::{Deserialize, Serialize}; use sqlx::{FromRow, Pool, Sqlite}; -/// Handler for startup, first creature_core initialization. Then it shouldn't be used - pub async fn update_creature_core_table(conn: &Pool) -> Result<()> { + assert_has_not_been_called!( + "Handler for startup, first creature_core initialization. Then it shouldn't be used" + ); let scales = fetch_creature_scales(conn).await?; for cr in get_creatures_raw_essential_data(conn, 0, -1).await? { let traits = fetch_creature_traits(conn, cr.id).await?; From 16f19b8b236afeb951a22a4a255fd678353f468a Mon Sep 17 00:00:00 2001 From: rakuja Date: Sun, 1 Dec 2024 18:42:13 +0100 Subject: [PATCH 02/18] feat: add senses data and vision field --- src/db/data_providers/creature_fetcher.rs | 21 ++++++++++++++----- .../creature_component/creature_extra.rs | 4 +++- src/models/db/mod.rs | 2 +- src/models/db/raw_sense.rs | 7 ------- src/models/db/sense.rs | 11 ++++++++++ 5 files changed, 31 insertions(+), 14 deletions(-) delete mode 100644 src/models/db/raw_sense.rs create mode 100644 src/models/db/sense.rs diff --git a/src/db/data_providers/creature_fetcher.rs b/src/db/data_providers/creature_fetcher.rs index 322777c..e866929 100644 --- a/src/db/data_providers/creature_fetcher.rs +++ b/src/db/data_providers/creature_fetcher.rs @@ -23,9 +23,9 @@ use crate::models::creature::items::spell_caster_entry::SpellCasterEntry; use crate::models::db::raw_immunity::RawImmunity; use crate::models::db::raw_language::RawLanguage; use crate::models::db::raw_resistance::RawResistance; -use crate::models::db::raw_sense::RawSense; use crate::models::db::raw_speed::RawSpeed; use crate::models::db::raw_weakness::RawWeakness; +use crate::models::db::sense::Sense; use crate::models::item::armor_struct::Armor; use crate::models::item::item_struct::Item; use crate::models::item::shield_struct::Shield; @@ -83,10 +83,10 @@ async fn fetch_creature_resistances( .await?) } -async fn fetch_creature_senses(conn: &Pool, creature_id: i64) -> Result> { +async fn fetch_creature_senses(conn: &Pool, creature_id: i64) -> Result> { Ok(sqlx::query_as!( - RawSense, - "SELECT * FROM SENSE_TABLE INTERSECT SELECT sense_id FROM SENSE_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1)", + Sense, + "SELECT * FROM SENSE_TABLE WHERE id IN (SELECT sense_id FROM SENSE_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1))", creature_id ).fetch_all(conn).await?) } @@ -188,6 +188,15 @@ async fn fetch_creature_perception(conn: &Pool, creature_id: i64) -> Res ) } +async fn fetch_creature_vision(conn: &Pool, creature_id: i64) -> Result { + Ok( + sqlx::query_scalar("SELECT vision FROM CREATURE_TABLE WHERE id = $1 LIMIT 1") + .bind(creature_id) + .fetch_one(conn) + .await?, + ) +} + async fn fetch_creature_perception_detail( conn: &Pool, creature_id: i64, @@ -577,6 +586,7 @@ pub async fn fetch_creature_extra_data( let ac_detail = fetch_creature_ac_detail(conn, creature_id).await?; let language_detail = fetch_creature_language_detail(conn, creature_id).await?; let perception = fetch_creature_perception(conn, creature_id).await?; + let has_vision = fetch_creature_vision(conn, creature_id).await?; let perception_detail = fetch_creature_perception_detail(conn, creature_id).await?; Ok(CreatureExtraData { @@ -584,7 +594,7 @@ pub async fn fetch_creature_extra_data( skills, items, languages: languages.iter().map(|x| x.name.clone()).collect(), - senses: senses.iter().map(|x| x.name.clone()).collect(), + senses, speeds: speeds .iter() .map(|x| (x.name.clone(), x.value as i16)) @@ -595,6 +605,7 @@ pub async fn fetch_creature_extra_data( language_detail, perception, perception_detail, + has_vision, }) } diff --git a/src/models/creature/creature_component/creature_extra.rs b/src/models/creature/creature_component/creature_extra.rs index 6d57bd4..85d0349 100644 --- a/src/models/creature/creature_component/creature_extra.rs +++ b/src/models/creature/creature_component/creature_extra.rs @@ -1,6 +1,7 @@ use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; use crate::models::creature::items::action::Action; use crate::models::creature::items::skill::Skill; +use crate::models::db::sense::Sense; use crate::models::item::item_struct::Item; use serde::{Deserialize, Serialize}; #[allow(unused_imports)] // it's actually used in the example schema @@ -30,7 +31,7 @@ pub struct CreatureExtraData { pub skills: Vec, pub items: Vec, pub languages: Vec, - pub senses: Vec, + pub senses: Vec, #[schema(example = json!({"fly": 100, "swim": 50, "Base": 25}))] pub speeds: BTreeMap, pub ability_scores: AbilityScores, @@ -40,6 +41,7 @@ pub struct CreatureExtraData { #[schema(example = 0)] pub perception: i8, pub perception_detail: Option, + pub has_vision: bool, } impl CreatureExtraData { diff --git a/src/models/db/mod.rs b/src/models/db/mod.rs index 4891214..471eb1c 100644 --- a/src/models/db/mod.rs +++ b/src/models/db/mod.rs @@ -1,6 +1,6 @@ pub mod raw_immunity; pub mod raw_language; pub mod raw_resistance; -pub mod raw_sense; pub mod raw_speed; pub mod raw_weakness; +pub mod sense; diff --git a/src/models/db/raw_sense.rs b/src/models/db/raw_sense.rs deleted file mode 100644 index 0ed414b..0000000 --- a/src/models/db/raw_sense.rs +++ /dev/null @@ -1,7 +0,0 @@ -use serde::{Deserialize, Serialize}; -use sqlx::FromRow; - -#[derive(Serialize, Deserialize, FromRow)] -pub struct RawSense { - pub name: String, -} diff --git a/src/models/db/sense.rs b/src/models/db/sense.rs new file mode 100644 index 0000000..8a24e74 --- /dev/null +++ b/src/models/db/sense.rs @@ -0,0 +1,11 @@ +use serde::{Deserialize, Serialize}; +use sqlx::FromRow; +use utoipa::ToSchema; + +#[derive(Serialize, Deserialize, FromRow, ToSchema, Clone, Eq, Hash, PartialEq)] +pub struct Sense { + pub id: i64, + pub name: String, + pub range: Option, + pub acuity: Option, +} From ab2ef96e4bfdc3e3e98bffe9879e1d8299e91ce6 Mon Sep 17 00:00:00 2001 From: rakuja Date: Sat, 7 Dec 2024 21:09:23 +0100 Subject: [PATCH 03/18] doc: update swagger --- src/routes/bestiary.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/bestiary.rs b/src/routes/bestiary.rs index 9aa98a6..a341f01 100644 --- a/src/routes/bestiary.rs +++ b/src/routes/bestiary.rs @@ -29,6 +29,7 @@ use crate::models::item::weapon_struct::Weapon; use crate::models::bestiary_structs::CreatureSortEnum; use crate::models::bestiary_structs::{BestiaryPaginatedRequest, BestiarySortData}; +use crate::models::db::sense::Sense; use crate::models::routers_validator_structs::{CreatureFieldFilters, PaginatedRequest}; use crate::services::bestiary_service; use crate::services::bestiary_service::BestiaryResponse; @@ -87,6 +88,7 @@ pub fn init_docs(doc: &mut utoipa::openapi::OpenApi) { CreatureExtraData, CreatureCombatData, CreatureSpellCasterData, + Sense, Spell, Shield, Weapon, From fdab4eac738fe398ce4722478f9d8723bc3cade9 Mon Sep 17 00:00:00 2001 From: rakuja Date: Sat, 7 Dec 2024 22:44:46 +0100 Subject: [PATCH 04/18] feat: align bestiary listing API with shop, moving to POST and allowing array fot filters --- Cargo.toml | 1 + src/models/creature/creature_struct.rs | 51 ++++++++-------- src/models/item/item_struct.rs | 11 ++-- src/models/routers_validator_structs.rs | 26 ++++----- src/routes/bestiary.rs | 77 ++++++++++++++++++------- src/routes/shop.rs | 11 +--- src/services/bestiary_service.rs | 2 +- src/services/mod.rs | 1 + src/services/sanitizer.rs | 9 +++ src/services/url_calculator.rs | 24 -------- 10 files changed, 115 insertions(+), 98 deletions(-) create mode 100644 src/services/sanitizer.rs diff --git a/Cargo.toml b/Cargo.toml index d3bbd44..f9b523f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ path = "src/main.rs" [lints.rust] unsafe_code = "forbid" +deprecated = "allow" [dependencies] actix-web = "4.9.0" diff --git a/src/models/creature/creature_struct.rs b/src/models/creature/creature_struct.rs index 71b6c2a..6b66fd7 100644 --- a/src/models/creature/creature_struct.rs +++ b/src/models/creature/creature_struct.rs @@ -120,20 +120,17 @@ impl Creature { } fn check_creature_pass_equality_filters(&self, filters: &CreatureFieldFilters) -> bool { - filters - .rarity_filter - .as_ref() - .map_or(true, |rarity| self.core_data.essential.rarity == *rarity) - && filters - .size_filter - .as_ref() - .map_or(true, |size| self.core_data.essential.size == *size) - && filters.alignment_filter.as_ref().map_or(true, |alignment| { - self.core_data.essential.alignment == *alignment - }) - && filters - .is_melee_filter - .map_or(true, |is_melee| self.core_data.derived.is_melee == is_melee) + filters.rarity_filter.as_ref().map_or(true, |x| { + x.iter() + .any(|rarity| self.core_data.essential.rarity == *rarity) + }) && filters.size_filter.as_ref().map_or(true, |x| { + x.iter().any(|size| self.core_data.essential.size == *size) + }) && filters.alignment_filter.as_ref().map_or(true, |x| { + x.iter() + .any(|align| self.core_data.essential.alignment == *align) + }) && filters + .is_melee_filter + .map_or(true, |is_melee| self.core_data.derived.is_melee == is_melee) && filters.is_ranged_filter.map_or(true, |is_ranged| { self.core_data.derived.is_ranged == is_ranged }) @@ -142,14 +139,14 @@ impl Creature { .map_or(true, |is_spell_caster| { self.core_data.derived.is_spell_caster == is_spell_caster }) - && filters - .type_filter - .as_ref() - .map_or(true, |cr_type| self.core_data.essential.cr_type == *cr_type) + && filters.type_filter.as_ref().map_or(true, |x| { + x.iter() + .any(|cr_type| self.core_data.essential.cr_type == *cr_type) + }) && (filters.role_threshold.is_none() || filters.role_filter.as_ref().map_or(true, |cr_role| { let t = filters.role_threshold.unwrap_or(0); - match cr_role { + cr_role.iter().any(|role| match role { CreatureRoleEnum::Brute => self.core_data.derived.brute_percentage >= t, CreatureRoleEnum::MagicalStriker => { self.core_data.derived.magical_striker_percentage >= t @@ -165,7 +162,7 @@ impl Creature { CreatureRoleEnum::SpellCaster => { self.core_data.derived.spell_caster_percentage >= t } - } + }) })) && match filters.pathfinder_version.clone().unwrap_or_default() { PathfinderVersionEnum::Legacy => !self.core_data.essential.remaster, @@ -176,13 +173,19 @@ impl Creature { fn check_creature_pass_string_filters(&self, filters: &CreatureFieldFilters) -> bool { filters.name_filter.as_ref().map_or(true, |name| { - self.core_data.essential.name.to_lowercase().contains(name) - }) && filters.family_filter.as_ref().map_or(true, |name| { self.core_data .essential - .family + .name .to_lowercase() - .contains(name) + .contains(name.to_lowercase().as_str()) + }) && filters.family_filter.as_ref().map_or(true, |x| { + x.iter().any(|fam| { + self.core_data + .essential + .family + .to_lowercase() + .contains(fam.to_lowercase().as_str()) + }) }) } } diff --git a/src/models/item/item_struct.rs b/src/models/item/item_struct.rs index 40ae12e..a35559b 100644 --- a/src/models/item/item_struct.rs +++ b/src/models/item/item_struct.rs @@ -156,6 +156,11 @@ impl Item { .type_filter .as_ref() .map_or(true, |x| x.iter().any(|t_filt| self.item_type == *t_filt)) + && match filters.pathfinder_version.clone().unwrap_or_default() { + PathfinderVersionEnum::Legacy => !self.remaster, + PathfinderVersionEnum::Remaster => self.remaster, + PathfinderVersionEnum::Any => true, + } } fn check_item_pass_string_filters(&self, filters: &ItemFieldFilters) -> bool { @@ -171,11 +176,7 @@ impl Item { .to_lowercase() .contains(cat.to_lowercase().as_str()) }) - }) && match filters.pathfinder_version.clone().unwrap_or_default() { - PathfinderVersionEnum::Legacy => !self.remaster, - PathfinderVersionEnum::Remaster => self.remaster, - PathfinderVersionEnum::Any => true, - } && filters.source_filter.as_ref().map_or(true, |x| { + }) && filters.source_filter.as_ref().map_or(true, |x| { x.iter().any(|source| { self.source .to_lowercase() diff --git a/src/models/routers_validator_structs.rs b/src/models/routers_validator_structs.rs index fa0f5b1..37ade05 100644 --- a/src/models/routers_validator_structs.rs +++ b/src/models/routers_validator_structs.rs @@ -8,25 +8,25 @@ use crate::models::shared::size_enum::SizeEnum; use serde::{Deserialize, Serialize}; use strum::Display; use utoipa::{IntoParams, ToSchema}; -#[derive(Serialize, Deserialize, IntoParams)] +#[derive(Serialize, Deserialize, IntoParams, ToSchema)] pub struct CreatureFieldFilters { pub name_filter: Option, - pub source_filter: Option, - pub family_filter: Option, - pub rarity_filter: Option, - pub size_filter: Option, - pub alignment_filter: Option, - pub role_filter: Option, - pub type_filter: Option, - #[param(minimum = 0, maximum = 100, example = 50)] + pub source_filter: Option>, + pub family_filter: Option>, + pub rarity_filter: Option>, + pub size_filter: Option>, + pub alignment_filter: Option>, + pub role_filter: Option>, + pub type_filter: Option>, + #[schema(minimum = 0, maximum = 100, example = 50)] pub role_threshold: Option, - #[param(minimum = 0, example = 0)] + #[schema(minimum = 0, example = 0)] pub min_hp_filter: Option, - #[param(minimum = 0, example = 100)] + #[schema(minimum = 0, example = 100)] pub max_hp_filter: Option, - #[param(minimum = -1, example = -1)] + #[schema(minimum = -1, example = -1)] pub min_level_filter: Option, - #[param(minimum = -1, example = 5)] + #[schema(minimum = -1, example = 5)] pub max_level_filter: Option, pub is_melee_filter: Option, pub is_ranged_filter: Option, diff --git a/src/routes/bestiary.rs b/src/routes/bestiary.rs index a341f01..8a0ff22 100644 --- a/src/routes/bestiary.rs +++ b/src/routes/bestiary.rs @@ -33,9 +33,10 @@ use crate::models::db::sense::Sense; use crate::models::routers_validator_structs::{CreatureFieldFilters, PaginatedRequest}; use crate::services::bestiary_service; use crate::services::bestiary_service::BestiaryResponse; +use crate::services::sanitizer::sanitize_id; use crate::AppState; use actix_web::web::Query; -use actix_web::{error, get, web, Responder, Result}; +use actix_web::{get, post, web, Responder}; use utoipa::OpenApi; pub fn init_endpoints(cfg: &mut web::ServiceConfig) { @@ -61,6 +62,7 @@ pub fn init_docs(doc: &mut utoipa::openapi::OpenApi) { #[openapi( paths( get_bestiary, + get_bestiary_listing, get_families_list, get_traits_list, get_sources_list, @@ -122,14 +124,15 @@ pub fn init_docs(doc: &mut utoipa::openapi::OpenApi) { ), )] #[get("/list")] +#[deprecated(since = "2.4.0", note = "please use `get_bestiary_listing` instead")] pub async fn get_bestiary( data: web::Data, filters: Query, pagination: Query, sort_data: Query, -) -> Result { +) -> actix_web::Result { Ok(web::Json( - bestiary_service::get_bestiary( + bestiary_service::get_bestiary_listing( &data, &filters.0, &BestiaryPaginatedRequest { @@ -141,6 +144,42 @@ pub async fn get_bestiary( )) } +#[utoipa::path( + post, + path = "/bestiary/list", + tag = "bestiary", + request_body( + content = CreatureFieldFilters, + content_type = "application/json" + ), + params( + PaginatedRequest, BestiarySortData + ), + responses( + (status=200, description = "Successful Response", body = BestiaryResponse), + (status=400, description = "Bad request.") + ), +)] +#[post("/list")] +pub async fn get_bestiary_listing( + data: web::Data, + web::Json(body): web::Json, + pagination: Query, + sort_data: Query, +) -> actix_web::Result { + Ok(web::Json( + bestiary_service::get_bestiary_listing( + &data, + &body, + &BestiaryPaginatedRequest { + paginated_request: pagination.0, + bestiary_sort_data: sort_data.0, + }, + ) + .await, + )) +} + #[utoipa::path( get, path = "/bestiary/families", @@ -154,7 +193,7 @@ pub async fn get_bestiary( ), )] #[get("/families")] -pub async fn get_families_list(data: web::Data) -> Result { +pub async fn get_families_list(data: web::Data) -> actix_web::Result { Ok(web::Json(bestiary_service::get_families_list(&data).await)) } @@ -171,7 +210,7 @@ pub async fn get_families_list(data: web::Data) -> Result) -> Result { +pub async fn get_traits_list(data: web::Data) -> actix_web::Result { Ok(web::Json(bestiary_service::get_traits_list(&data).await)) } @@ -188,7 +227,7 @@ pub async fn get_traits_list(data: web::Data) -> Result) -> Result { +pub async fn get_sources_list(data: web::Data) -> actix_web::Result { Ok(web::Json(bestiary_service::get_sources_list(&data).await)) } @@ -205,7 +244,7 @@ pub async fn get_sources_list(data: web::Data) -> Result) -> Result { +pub async fn get_rarities_list(data: web::Data) -> actix_web::Result { Ok(web::Json(bestiary_service::get_rarities_list(&data).await)) } @@ -222,7 +261,7 @@ pub async fn get_rarities_list(data: web::Data) -> Result) -> Result { +pub async fn get_sizes_list(data: web::Data) -> actix_web::Result { Ok(web::Json(bestiary_service::get_sizes_list(&data).await)) } @@ -239,7 +278,7 @@ pub async fn get_sizes_list(data: web::Data) -> Result ), )] #[get("/alignments")] -pub async fn get_alignments_list(data: web::Data) -> Result { +pub async fn get_alignments_list(data: web::Data) -> actix_web::Result { Ok(web::Json( bestiary_service::get_alignments_list(&data).await, )) @@ -258,7 +297,9 @@ pub async fn get_alignments_list(data: web::Data) -> Result) -> Result { +pub async fn get_creature_types_list( + data: web::Data, +) -> actix_web::Result { Ok(web::Json( bestiary_service::get_creature_types_list(&data).await, )) @@ -277,7 +318,7 @@ pub async fn get_creature_types_list(data: web::Data) -> Result Result { +pub async fn get_creature_roles_list() -> actix_web::Result { Ok(web::Json(bestiary_service::get_creature_roles_list().await)) } @@ -299,7 +340,7 @@ pub async fn get_creature( data: web::Data, creature_id: web::Path, response_data_mods: Query, -) -> Result { +) -> actix_web::Result { Ok(web::Json( bestiary_service::get_creature(&data, sanitize_id(&creature_id)?, &response_data_mods.0) .await, @@ -324,7 +365,7 @@ pub async fn get_elite_creature( data: web::Data, creature_id: web::Path, response_data_mods: Query, -) -> Result { +) -> actix_web::Result { Ok(web::Json( bestiary_service::get_elite_creature( &data, @@ -353,7 +394,7 @@ pub async fn get_weak_creature( data: web::Data, creature_id: web::Path, response_data_mods: Query, -) -> Result { +) -> actix_web::Result { Ok(web::Json( bestiary_service::get_weak_creature( &data, @@ -363,11 +404,3 @@ pub async fn get_weak_creature( .await, )) } - -fn sanitize_id(creature_id: &str) -> Result { - let id = creature_id.parse::(); - match id { - Ok(s) => Ok(s), - Err(e) => Err(error::ErrorNotFound(e)), - } -} diff --git a/src/routes/shop.rs b/src/routes/shop.rs index d4689d8..da830e7 100644 --- a/src/routes/shop.rs +++ b/src/routes/shop.rs @@ -12,11 +12,12 @@ use crate::models::shop_structs::ShopTemplateData; use crate::models::shop_structs::ShopTemplateEnum; use crate::models::shop_structs::{ItemSortEnum, ShopPaginatedRequest}; use crate::models::shop_structs::{RandomShopData, ShopSortData}; +use crate::services::sanitizer::sanitize_id; use crate::services::shop_service; use crate::services::shop_service::ShopListingResponse; use crate::AppState; use actix_web::web::Query; -use actix_web::{error, get, post, web, Responder}; +use actix_web::{get, post, web, Responder}; use utoipa::OpenApi; pub fn init_endpoints(cfg: &mut web::ServiceConfig) { @@ -201,11 +202,3 @@ pub async fn get_items_traits_list(data: web::Data) -> actix_web::Resu pub async fn get_templates_data() -> actix_web::Result { Ok(web::Json(shop_service::get_shop_templates_data().await)) } - -fn sanitize_id(creature_id: &str) -> actix_web::Result { - let id = creature_id.parse::(); - match id { - Ok(s) => Ok(s), - Err(e) => Err(error::ErrorNotFound(e)), - } -} diff --git a/src/services/bestiary_service.rs b/src/services/bestiary_service.rs index 1eabff1..566fffd 100644 --- a/src/services/bestiary_service.rs +++ b/src/services/bestiary_service.rs @@ -54,7 +54,7 @@ pub async fn get_weak_creature( } } -pub async fn get_bestiary( +pub async fn get_bestiary_listing( app_state: &AppState, field_filter: &CreatureFieldFilters, pagination: &BestiaryPaginatedRequest, diff --git a/src/services/mod.rs b/src/services/mod.rs index 8eb26ed..3b56e67 100644 --- a/src/services/mod.rs +++ b/src/services/mod.rs @@ -1,5 +1,6 @@ pub mod bestiary_service; pub mod encounter_handler; pub mod encounter_service; +pub mod sanitizer; pub mod shop_service; pub mod url_calculator; diff --git a/src/services/sanitizer.rs b/src/services/sanitizer.rs new file mode 100644 index 0000000..6d0dea4 --- /dev/null +++ b/src/services/sanitizer.rs @@ -0,0 +1,9 @@ +use actix_web::error; + +pub fn sanitize_id(creature_id: &str) -> actix_web::Result { + let id = creature_id.parse::(); + match id { + Ok(s) => Ok(s), + Err(e) => Err(error::ErrorNotFound(e)), + } +} diff --git a/src/services/url_calculator.rs b/src/services/url_calculator.rs index 5ae554e..20af47b 100644 --- a/src/services/url_calculator.rs +++ b/src/services/url_calculator.rs @@ -65,30 +65,6 @@ fn creature_filter_query_calculator(field_filters: &CreatureFieldFilters) -> Str .name_filter .clone() .map(|name| format!("name_filter={}", name)), - field_filters - .family_filter - .clone() - .map(|fam| format!("family_filter={}", fam)), - field_filters - .rarity_filter - .clone() - .map(|rar| format!("rarity_filter={}", rar)), - field_filters - .size_filter - .clone() - .map(|size| format!("size_filter={}", size)), - field_filters - .alignment_filter - .clone() - .map(|align| format!("alignment_filter={}", align)), - field_filters - .role_filter - .clone() - .map(|role| format!("role_filter={}", role)), - field_filters - .type_filter - .clone() - .map(|cr_type| format!("type_filter={}", cr_type)), field_filters .role_threshold .map(|threshold| format!("role_threshold={}", threshold)), From d22497dc02f0eac44598d9662cdbe496e05ec39c Mon Sep 17 00:00:00 2001 From: rakuja Date: Sat, 7 Dec 2024 23:06:09 +0100 Subject: [PATCH 05/18] fix: update DB version to download --- Makefile.toml | 2 +- setup.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile.toml b/Makefile.toml index 875268d..71c2a01 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -4,7 +4,7 @@ args = ["clean"] [tasks.prebuild] command = "python3" -args = ["setup.py", "--db_version", "2.3.0"] +args = ["setup.py", "--db_version", "2.4.0"] [tasks.format] install_crate = "rustfmt" diff --git a/setup.py b/setup.py index fc9d09e..ae863b6 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def handle_command_line_arguments() -> Optional[str]: if currentArgument in ("-h", "--help"): print("This script downloads or creates necessary file to build BYBE. \n" "Should be executed the first time the project is built in the machine or when resetting the database \n" - "Pass the --db_version or -v argument to input a specific BYBE-DB version to download (>= 2.3.0)") + "Pass the --db_version or -d argument to input a specific BYBE-DB version to download (>= 2.3.0)") elif currentArgument in ("-d", "--db_version"): return currentValue except getopt.error: @@ -33,8 +33,8 @@ def handle_command_line_arguments() -> Optional[str]: def main(): # Check if the file already exists or needs downloading - db_version: str = handle_command_line_arguments() or "2.3.0" - print(db_version) + db_version: str = handle_command_line_arguments() + print(f"Using DB version: {db_version}") or "2.3.0" # Oldest DB version publicly available remote_url: str = f"https://github.com/RakuJa/BYBE-DB/releases/download/v{db_version}/database.db" destination_file: str = "database.db" if not os.path.exists(destination_file): From df3b091c4a8a86cfe79e719f9b9cf50b13dc0b36 Mon Sep 17 00:00:00 2001 From: rakuja Date: Sun, 8 Dec 2024 01:33:08 +0100 Subject: [PATCH 06/18] refactor: add pedantic rules --- Cargo.toml | 13 ++++ build/main.rs | 2 +- src/db/bestiary_proxy.rs | 17 +++-- src/db/data_providers/creature_fetcher.rs | 12 +-- src/db/data_providers/raw_query_builder.rs | 39 +++++----- src/db/data_providers/shop_fetcher.rs | 10 +-- src/db/shop_proxy.rs | 10 +-- src/lib.rs | 8 +- .../creature_component/creature_combat.rs | 10 ++- .../creature_component/creature_extra.rs | 8 +- .../creature_spell_caster.rs | 7 +- .../creature_metadata/creature_role.rs | 73 +++++++++---------- .../creature/creature_metadata/type_enum.rs | 1 - .../creature_metadata/variant_enum.rs | 33 +++++---- src/models/creature/creature_struct.rs | 10 +-- src/models/encounter_structs.rs | 1 + src/models/routers_validator_structs.rs | 19 ++--- src/models/shop_structs.rs | 36 ++------- src/routes/bestiary.rs | 2 +- src/routes/encounter.rs | 2 +- src/routes/shop.rs | 2 +- src/services/bestiary_service.rs | 9 ++- .../encounter_handler/difficulty_utilities.rs | 5 +- .../encounter_handler/encounter_calculator.rs | 69 ++++++++++-------- src/services/encounter_service.rs | 57 +++++++++------ src/services/shop_service.rs | 52 +++++++------ src/services/url_calculator.rs | 58 +++++++-------- 27 files changed, 289 insertions(+), 276 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f9b523f..d72cba3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,19 @@ path = "src/main.rs" unsafe_code = "forbid" deprecated = "allow" +[lints.clippy] +struct_field_names = {level = "allow", priority = 2} +module_name_repetitions = {level = "allow", priority = 2} +# We are ok with losing some data without wrapping. We are not ok with wrapping +# Ex: 128.12 => 128 is ok, 128 => 0 is not. +cast_possible_truncation = {level = "allow", priority = 2} +cast_precision_loss = {level = "allow", priority = 2} + +pedantic = {level = "warn", priority = 1} +#restriction = "warn" +# nursery = "warn" + + [dependencies] actix-web = "4.9.0" actix-cors = "0.7.0" diff --git a/build/main.rs b/build/main.rs index c40d67c..bfc7afa 100644 --- a/build/main.rs +++ b/build/main.rs @@ -27,5 +27,5 @@ async fn main() { .expect("Could not populate the db, something went wrong.."); creature_core_db_init::cleanup_db(&conn) .await - .expect("Could not clean up the db. Dirty state detected, closing..") + .expect("Could not clean up the db. Dirty state detected, closing.."); } diff --git a/src/db/bestiary_proxy.rs b/src/db/bestiary_proxy.rs index 8c6ce22..2464f31 100644 --- a/src/db/bestiary_proxy.rs +++ b/src/db/bestiary_proxy.rs @@ -109,7 +109,7 @@ pub async fn get_paginated_creatures( let curr_slice: Vec = filtered_list .iter() .skip(pagination.paginated_request.cursor as usize) - .take(pagination.paginated_request.page_size as usize) + .take(pagination.paginated_request.page_size.unsigned_abs() as usize) .cloned() .collect(); @@ -163,9 +163,9 @@ pub async fn get_all_possible_values_of_filter( let mut x = match field { CreatureFilter::Size => runtime_fields_values.list_of_sizes, CreatureFilter::Rarity => runtime_fields_values.list_of_rarities, - CreatureFilter::Ranged => vec![true.to_string(), false.to_string()], - CreatureFilter::Melee => vec![true.to_string(), false.to_string()], - CreatureFilter::SpellCaster => vec![true.to_string(), false.to_string()], + CreatureFilter::Ranged | CreatureFilter::Melee | CreatureFilter::SpellCaster => { + vec![true.to_string(), false.to_string()] + } CreatureFilter::Family => runtime_fields_values.list_of_families, CreatureFilter::Traits => runtime_fields_values.list_of_traits, CreatureFilter::Sources => runtime_fields_values.list_of_sources, @@ -240,21 +240,22 @@ async fn get_list(app_state: &AppState, variant: CreatureVariant) -> Vec creatures.into_iter().map(Creature::from_core).collect(), _ => creatures .into_iter() - .map(|cr| Creature::from_core_with_variant(cr, variant.clone())) + .map(|cr| Creature::from_core_with_variant(cr, variant)) .collect(), }; } vec![] } -pub fn order_list_by_level(creature_list: Vec) -> HashMap> { +pub fn order_list_by_level(creature_list: &[Creature]) -> HashMap> { let mut ordered_by_level = HashMap::new(); - creature_list.iter().for_each(|creature| { + + for creature in creature_list { ordered_by_level .entry(creature.variant_data.level) .or_insert_with(Vec::new) .push(creature.clone()); - }); + } ordered_by_level } diff --git a/src/db/data_providers/creature_fetcher.rs b/src/db/data_providers/creature_fetcher.rs index e866929..2b60bf4 100644 --- a/src/db/data_providers/creature_fetcher.rs +++ b/src/db/data_providers/creature_fetcher.rs @@ -246,7 +246,7 @@ async fn fetch_creature_weapons(conn: &Pool, creature_id: i64) -> Result el.weapon_data.damage_data = fetch_weapon_damage_data(conn, el.weapon_data.id) .await .unwrap_or(vec![]); - result_vec.push(el) + result_vec.push(el); } Ok(result_vec) } @@ -277,7 +277,7 @@ async fn fetch_creature_armors(conn: &Pool, creature_id: i64) -> Result< el.armor_data.property_runes = fetch_armor_runes(conn, el.armor_data.id) .await .unwrap_or(vec![]); - result_vec.push(el) + result_vec.push(el); } Ok(result_vec) } @@ -304,7 +304,7 @@ async fn fetch_creature_shields(conn: &Pool, creature_id: i64) -> Result .await .unwrap_or(vec![]); el.item_core.quantity = fetch_shield_quantity(conn, creature_id, el.shield_data.id).await; - result_vec.push(el) + result_vec.push(el); } Ok(result_vec) } @@ -326,7 +326,7 @@ async fn fetch_creature_items(conn: &Pool, creature_id: i64) -> Result { if !simple_core_query.is_empty() { - simple_core_query.push_str(" AND ") + simple_core_query.push_str(" AND "); } simple_core_query.push_str( prepare_in_statement_for_generic_type(key.to_string().as_str(), value.iter()) .as_str(), - ) + ); } CreatureFilter::Family | CreatureFilter::Alignment @@ -75,7 +75,7 @@ pub fn prepare_filtered_get_creatures_core( | CreatureFilter::Rarity | CreatureFilter::CreatureTypes => { if !simple_core_query.is_empty() { - simple_core_query.push_str(" AND ") + simple_core_query.push_str(" AND "); } simple_core_query.push_str( prepare_case_insensitive_in_statement( @@ -83,24 +83,24 @@ pub fn prepare_filtered_get_creatures_core( value.iter().cloned(), ) .as_str(), - ) + ); } CreatureFilter::Traits => { - trait_query.push_str(prepare_creature_trait_filter(value.iter().cloned()).as_str()) + trait_query.push_str(prepare_creature_trait_filter(value.iter().cloned()).as_str()); } CreatureFilter::CreatureRoles => { if !simple_core_query.is_empty() { - simple_core_query.push_str(" AND ") + simple_core_query.push_str(" AND "); } simple_core_query - .push_str(prepare_bounded_or_check(value, ACCURACY_THRESHOLD, 100).as_str()) + .push_str(prepare_bounded_or_check(value, ACCURACY_THRESHOLD, 100).as_str()); } - _ => (), + CreatureFilter::Sources => (), // Never given as value to filter } } let mut where_query = simple_core_query.to_string(); if !trait_query.is_empty() { - where_query.push_str(format!(" AND id IN ({trait_query}) GROUP BY cc.id").as_str()) + where_query.push_str(format!(" AND id IN ({trait_query}) GROUP BY cc.id").as_str()); } if !where_query.is_empty() { where_query = format!("WHERE {where_query}"); @@ -198,7 +198,7 @@ where if result_string.ends_with(',') { result_string.remove(result_string.len() - 1); } - result_string.push(')') + result_string.push(')'); } result_string } @@ -224,7 +224,7 @@ where if result_string.ends_with(',') { result_string.remove(result_string.len() - 1); } - result_string.push(')') + result_string.push(')'); } result_string } @@ -272,8 +272,8 @@ fn prepare_item_filter_statement(shop_filter_vectors: &ItemTableFieldsFilter) -> prepare_case_insensitive_in_statement("source", shop_filter_vectors.source_filter.iter()), prepare_bounded_check( &String::from("level"), - shop_filter_vectors.min_level as i64, - shop_filter_vectors.max_level as i64, + i64::from(shop_filter_vectors.min_level), + i64::from(shop_filter_vectors.max_level), ), ] .into_iter() @@ -283,7 +283,7 @@ fn prepare_item_filter_statement(shop_filter_vectors: &ItemTableFieldsFilter) -> if filters_query.is_empty() { remaster_query } else { - format!("{} AND {}", remaster_query, filters_query) + format!("{remaster_query} AND {filters_query}") } } @@ -301,14 +301,11 @@ where if whitelist_query.is_empty() && blacklist_query.is_empty() { String::new() } else if whitelist_query.is_empty() { - format!("id NOT IN ({})", blacklist_query) + format!("id NOT IN ({blacklist_query})") } else if blacklist_query.is_empty() { - format!("id IN ({})", whitelist_query) + format!("id IN ({whitelist_query})") } else { - format!( - "id IN ({}) AND id NOT IN ({})", - whitelist_query, blacklist_query - ) + format!("id IN ({whitelist_query}) AND id NOT IN ({blacklist_query})") } } @@ -320,7 +317,7 @@ fn prepare_get_id_matching_item_type_query(item_type: &ItemTypeEnum) -> String { // There is no need for an and statement here, we already fetch from the "private" table. // Item instead contains a lot of item_type (it's the base for weapon/shield/etc) ItemTypeEnum::Weapon | ItemTypeEnum::Armor | ItemTypeEnum::Shield => { - ("base_item_id", "".to_string()) + ("base_item_id", String::new()) } }; let tass_item_id_field = match item_type { diff --git a/src/db/data_providers/shop_fetcher.rs b/src/db/data_providers/shop_fetcher.rs index 9da37cb..3df257c 100644 --- a/src/db/data_providers/shop_fetcher.rs +++ b/src/db/data_providers/shop_fetcher.rs @@ -160,7 +160,7 @@ pub async fn fetch_weapons( result_vec.push(Weapon { item_core: el.item_core, weapon_data: el.weapon_data, - }) + }); } Ok(result_vec) } @@ -190,7 +190,7 @@ pub async fn fetch_armors(conn: &Pool, cursor: u32, page_size: i16) -> R result_vec.push(Armor { item_core: el.item_core, armor_data: el.armor_data, - }) + }); } Ok(result_vec) } @@ -222,7 +222,7 @@ pub async fn fetch_shields( result_vec.push(Shield { item_core: el.item_core, shield_data: el.shield_data, - }) + }); } Ok(result_vec) } @@ -267,7 +267,7 @@ pub async fn fetch_items_with_filters( + filters.n_of_weapons + filters.n_of_armors + filters.n_of_consumables; - if items.len() as i64 >= n_of_items_to_return { + if i64::try_from(items.len()).unwrap_or(i64::MAX) >= n_of_items_to_return { debug!("Result vector is the correct size, no more operations needed"); return Ok(items); } @@ -284,7 +284,7 @@ pub async fn fetch_items_with_filters( fn fill_item_vec_to_len(item_vec: &[&Item], desired_len: i64) -> Vec { let mut og_vec: Vec = item_vec.iter().map(|x| (*x).clone()).collect(); - for _ in 0..(item_vec.len() as i64 - desired_len) { + for _ in 0..(i64::try_from(item_vec.len()).unwrap_or(i64::MAX) - desired_len) { if let Some(x) = item_vec.get(fastrand::usize(0..item_vec.len())) { og_vec.push((*x).clone()); } diff --git a/src/db/shop_proxy.rs b/src/db/shop_proxy.rs index 7487068..224d18e 100644 --- a/src/db/shop_proxy.rs +++ b/src/db/shop_proxy.rs @@ -67,7 +67,7 @@ pub async fn get_paginated_items( let curr_slice: Vec = filtered_list .iter() .skip(pagination.paginated_request.cursor as usize) - .take(pagination.paginated_request.page_size as usize) + .take(pagination.paginated_request.page_size.unsigned_abs() as usize) .cloned() .collect(); @@ -104,7 +104,7 @@ async fn get_list(app_state: &AppState) -> Vec { weapon_data: None, armor_data: None, shield_data: None, - }) + }); } for el in get_all_weapons_from_db(app_state).await.unwrap_or(vec![]) { response_vec.push(ResponseItem { @@ -112,7 +112,7 @@ async fn get_list(app_state: &AppState) -> Vec { weapon_data: Some(el.weapon_data), armor_data: None, shield_data: None, - }) + }); } for el in get_all_armors_from_db(app_state).await.unwrap_or(vec![]) { response_vec.push(ResponseItem { @@ -120,7 +120,7 @@ async fn get_list(app_state: &AppState) -> Vec { weapon_data: None, armor_data: Some(el.armor_data), shield_data: None, - }) + }); } for el in get_all_shields_from_db(app_state).await.unwrap() { response_vec.push(ResponseItem { @@ -128,7 +128,7 @@ async fn get_list(app_state: &AppState) -> Vec { weapon_data: None, armor_data: None, shield_data: Some(el.shield_data), - }) + }); } response_vec } diff --git a/src/lib.rs b/src/lib.rs index dac3402..819f0f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,6 +17,10 @@ use std::num::NonZero; use utoipa::OpenApi; use utoipa_swagger_ui::SwaggerUi; +#[derive(OpenApi)] +#[openapi(paths(index))] +struct ApiDoc; + #[derive(Clone)] pub struct AppState { conn: Pool, @@ -139,10 +143,6 @@ pub async fn start( ); // Swagger initialization - #[derive(OpenApi)] - #[openapi(paths(index))] - struct ApiDoc; - let mut openapi = ApiDoc::openapi(); init_docs(&mut openapi); diff --git a/src/models/creature/creature_component/creature_combat.rs b/src/models/creature/creature_component/creature_combat.rs index fa8c8a4..abb56b8 100644 --- a/src/models/creature/creature_component/creature_combat.rs +++ b/src/models/creature/creature_component/creature_combat.rs @@ -49,7 +49,7 @@ impl CreatureCombatData { wp }) .collect(); - com_data.ac = (com_data.ac as i64 + modifier) as i8; + com_data.ac = (i64::from(com_data.ac) + modifier) as i8; com_data.saving_throws.fortitude += modifier; com_data.saving_throws.reflex += modifier; com_data.saving_throws.will += modifier; @@ -82,16 +82,18 @@ impl CreatureCombatData { com_data } - /// Lowers saving throws, weapon to hit bonus, and ac by the given pwl_mod + /// Lowers saving throws, weapon to hit bonus, and ac by the given `pwl_mod` pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureCombatData { - self.add_mod_to_saving_throws_and_ac_and_wp_to_hit(-(pwl_mod as i64)) + self.add_mod_to_saving_throws_and_ac_and_wp_to_hit( + -i64::try_from(pwl_mod).unwrap_or(i64::MAX), + ) } /// Increase/Decrease the damage of its Strikes and other offensive abilities by 2. /// If the creature has limits on how many times or how often it can use an ability /// (such as a spellcaster’s spells or a dragon’s breath), decrease the damage by 4 instead. /// Increase/Decrease the creature’s AC, attack modifiers, DCs, saving throws by 2. - pub fn convert_from_base_to_variant(self, variant: &CreatureVariant) -> CreatureCombatData { + pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> CreatureCombatData { let modifier = variant.to_adjustment_modifier(); self.add_mod_to_saving_throws_and_ac_and_wp_to_hit(modifier) .add_mod_to_dmg(modifier) diff --git a/src/models/creature/creature_component/creature_extra.rs b/src/models/creature/creature_component/creature_extra.rs index 85d0349..e3197c8 100644 --- a/src/models/creature/creature_component/creature_extra.rs +++ b/src/models/creature/creature_component/creature_extra.rs @@ -48,7 +48,7 @@ impl CreatureExtraData { fn add_mod_to_perception_and_skill_mods(self, modifier: i64) -> CreatureExtraData { let mut ex_data = self; // we should never have a pwl much greater than perception (pwl=lvl) - ex_data.perception = (ex_data.perception as i64 + modifier) as i8; + ex_data.perception = (i64::from(ex_data.perception) + modifier) as i8; ex_data.skills = ex_data .skills @@ -61,13 +61,13 @@ impl CreatureExtraData { ex_data } - /// Lowers skill and perception by the given pwl_mod + /// Lowers skill and perception by the given `pwl_mod` pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureExtraData { - self.add_mod_to_perception_and_skill_mods(-(pwl_mod as i64)) + self.add_mod_to_perception_and_skill_mods(-i64::try_from(pwl_mod).unwrap_or(i64::MAX)) } /// Increase/Decrease Perception, and skill modifiers by 2. - pub fn convert_from_base_to_variant(self, variant: &CreatureVariant) -> CreatureExtraData { + pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> CreatureExtraData { self.add_mod_to_perception_and_skill_mods(variant.to_adjustment_modifier()) } } diff --git a/src/models/creature/creature_component/creature_spell_caster.rs b/src/models/creature/creature_component/creature_spell_caster.rs index 3adfa65..ce43836 100644 --- a/src/models/creature/creature_component/creature_spell_caster.rs +++ b/src/models/creature/creature_component/creature_spell_caster.rs @@ -29,16 +29,13 @@ impl CreatureSpellCasterData { /// Lowers spell caster atk and dc pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureSpellCasterData { - self.add_mod_to_spellcaster_atk_and_dc(-(pwl_mod as i64)) + self.add_mod_to_spellcaster_atk_and_dc(-i64::try_from(pwl_mod).unwrap_or(i64::MAX)) } /// Increase/Decrease the damage of its Strikes and other offensive abilities by 2. /// If the creature has limits on how many times or how often it can use an ability /// (such as a spellcaster’s spells or a dragon’s breath), decrease the damage by 4 instead. - pub fn convert_from_base_to_variant( - self, - variant: &CreatureVariant, - ) -> CreatureSpellCasterData { + pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> CreatureSpellCasterData { self.add_mod_to_spellcaster_atk_and_dc(variant.to_adjustment_modifier()) } } diff --git a/src/models/creature/creature_metadata/creature_role.rs b/src/models/creature/creature_metadata/creature_role.rs index 88e98e9..4eef0e4 100644 --- a/src/models/creature/creature_metadata/creature_role.rs +++ b/src/models/creature/creature_metadata/creature_role.rs @@ -59,45 +59,37 @@ impl CreatureRoleEnum { let mut roles = BTreeMap::new(); roles.insert( Self::Brute, - is_brute(cr_core, cr_extra, cr_combat, scales) - .map(|x| (x * 100.).round() as i64) - .unwrap_or(0), + is_brute(cr_core, cr_extra, cr_combat, scales).map_or(0, |x| (x * 100.).round() as i64), ); roles.insert( Self::MagicalStriker, is_magical_striker(cr_core, cr_spells, cr_combat, scales) - .map(|x| (x * 100.).round() as i64) - .unwrap_or(0), + .map_or(0, |x| (x * 100.).round() as i64), ); roles.insert( Self::SkillParagon, is_skill_paragon(cr_core, cr_extra, cr_combat, scales) - .map(|x| (x * 100.).round() as i64) - .unwrap_or(0), + .map_or(0, |x| (x * 100.).round() as i64), ); roles.insert( Self::Skirmisher, is_skirmisher(cr_core, cr_extra, cr_combat, scales) - .map(|x| (x * 100.).round() as i64) - .unwrap_or(0), + .map_or(0, |x| (x * 100.).round() as i64), ); roles.insert( Self::Sniper, is_sniper(cr_core, cr_extra, cr_combat, scales) - .map(|x| (x * 100.).round() as i64) - .unwrap_or(0), + .map_or(0, |x| (x * 100.).round() as i64), ); roles.insert( Self::Soldier, is_soldier(cr_core, cr_extra, cr_combat, scales) - .map(|x| (x * 100.).round() as i64) - .unwrap_or(0), + .map_or(0, |x| (x * 100.).round() as i64), ); roles.insert( Self::SpellCaster, is_spellcaster(cr_core, cr_spells, cr_combat, cr_extra, scales) - .map(|x| (x * 100.).round() as i64) - .unwrap_or(0), + .map_or(0, |x| (x * 100.).round() as i64), ); roles } @@ -117,7 +109,7 @@ fn is_brute( let lvl = cr_core.base_level; let per_scales = scales.perception_scales.get(&lvl)?; // low Perception; - score += calculate_ub_distance(per_scales.moderate, cr_extra.perception as i64 + 1); + score += calculate_ub_distance(per_scales.moderate, i64::from(cr_extra.perception) + 1); let ability_scales = scales.ability_scales.get(&lvl)?; // high or extreme Str modifier, score += calculate_lb_distance(ability_scales.high, cr_extra.ability_scores.strength); @@ -143,7 +135,7 @@ fn is_brute( score += calculate_ub_distance(saving_scales.moderate, cr_combat.saving_throws.will + 1); let ac_scales = scales.ac_scales.get(&lvl)?; // moderate or low AC; - score += calculate_ub_distance(ac_scales.high, cr_combat.ac as i64 + 1); + score += calculate_ub_distance(ac_scales.high, i64::from(cr_combat.ac) + 1); // high HP; let hp_scales = scales.hp_scales.get(&lvl)?; score += calculate_lb_distance(hp_scales.high_lb, cr_core.hp); @@ -172,7 +164,7 @@ fn is_brute( .min(); score += wp_distance.unwrap_or(MISSING_FIELD_DISTANCE); - Some(f64::E().powf(-0.2 * (score as f64))) + Some(f64::E().powf(-0.2 * f64::from(score))) } // Sniper @@ -188,7 +180,7 @@ fn is_sniper( // high Perception (chosen moderate // !!!This is a critical stat, upping it will half creature result!!! // ); - score += calculate_lb_distance(per_scales.moderate, cr_extra.perception as i64); + score += calculate_lb_distance(per_scales.moderate, i64::from(cr_extra.perception)); let ability_scales = scales.ability_scales.get(&lvl)?; // high Dex modifier (chosen moderate); score += calculate_lb_distance(ability_scales.moderate, cr_extra.ability_scores.dexterity); @@ -217,7 +209,7 @@ fn is_sniper( }) .min(); score += wp_distance.unwrap_or(MISSING_FIELD_DISTANCE); - Some(f64::E().powf(-0.2 * (score as f64))) + Some(f64::E().powf(-0.2 * f64::from(score))) } // Skirmisher fn is_skirmisher( @@ -243,10 +235,10 @@ fn is_skirmisher( score += cr_extra .speeds .values() - .map(|speed_value| calculate_lb_distance(30, *speed_value as i64)) + .map(|speed_value| calculate_lb_distance(30, i64::from(*speed_value))) .min() .unwrap_or(MISSING_FIELD_DISTANCE); - Some(f64::E().powf(-0.2 * (score as f64))) + Some(f64::E().powf(-0.2 * f64::from(score))) } // Soldier pub fn is_soldier( @@ -262,7 +254,7 @@ pub fn is_soldier( score += calculate_lb_distance(ability_scales.high, cr_extra.ability_scores.strength); let ac_scales = scales.ac_scales.get(&lvl)?; // high to extreme AC; - score += calculate_lb_distance(ac_scales.high, cr_combat.ac as i64); + score += calculate_lb_distance(ac_scales.high, i64::from(cr_combat.ac)); let saving_scales = scales.saving_throw_scales.get(&lvl)?; // high Fortitude; score += calculate_lb_distance(saving_scales.high, cr_combat.saving_throws.fortitude); @@ -295,7 +287,7 @@ pub fn is_soldier( }) { score += 3; } - Some(f64::E().powf(-0.2 * (score as f64))) + Some(f64::E().powf(-0.2 * f64::from(score))) } // Magical Striker @@ -333,11 +325,12 @@ pub fn is_magical_striker( score += MISSING_FIELD_DISTANCE; } if (cr_spell.spells.len() as f64) < (cr_core.base_level as f64 / 2.).ceil() - 1. { - score += - (((cr_core.base_level as f64 / 2.).ceil() as i64) - 1 - (cr_spell.spells.len() as i64)) - .unsigned_abs() as u16; + score += (((cr_core.base_level as f64 / 2.).ceil() as i64) + - 1 + - (i64::try_from(cr_spell.spells.len()).unwrap_or(i64::MAX))) + .unsigned_abs() as u16; } - Some(f64::E().powf(-0.2 * (score as f64))) + Some(f64::E().powf(-0.2 * f64::from(score))) } // Skill Paragon @@ -371,13 +364,16 @@ fn is_skill_paragon( // many skills at moderate or high and potentially one or two extreme skills; // Many is kinda up in the air, I'll set 70% - let cr_skill_amount = cr_extra.skills.len() as i64 * 70 / 100; + let cr_skill_amount = i64::try_from(cr_extra.skills.len()).unwrap_or(i64::MAX) / 100 * 70; // if there aren't at least 70% of skill in the moderate-high range - score += (cr_extra - .skills - .iter() - .filter(|x| x.modifier >= saving_scales.moderate) - .count() as i64 + score += (i64::try_from( + cr_extra + .skills + .iter() + .filter(|x| x.modifier >= saving_scales.moderate) + .count(), + ) + .unwrap_or(i64::MAX) - cr_skill_amount) .unsigned_abs() as u16; // at least two special ability to use the creature's skills in combat @@ -394,7 +390,7 @@ fn is_skill_paragon( { score += MISSING_FIELD_DISTANCE; } - Some(f64::E().powf(-0.2 * (score as f64))) + Some(f64::E().powf(-0.2 * f64::from(score))) } // Spellcaster fn is_spellcaster( @@ -427,8 +423,9 @@ fn is_spellcaster( ); // prepared or spontaneous spells up to half the creature’s level (rounded up) if (cr_spell.spells.len() as f64) < (cr_core.base_level as f64 / 2.).ceil() { - score += (((cr_core.base_level as f64 / 2.).ceil() as i64) - (cr_spell.spells.len() as i64)) - .unsigned_abs() as u16; + score += (((cr_core.base_level as f64 / 2.).ceil() as i64) + - i64::try_from(cr_spell.spells.len()).unwrap_or(i64::MAX)) + .unsigned_abs() as u16; } let ability_scales = scales.ability_scales.get(&lvl)?; // high or extreme modifier for the corresponding mental ability; @@ -438,7 +435,7 @@ fn is_spellcaster( .max(cr_extra.ability_scores.intelligence) .max(cr_extra.ability_scores.charisma); score += calculate_lb_distance(ability_scales.high, best_mental_ability); - Some(f64::E().powf(-0.2 * (score as f64))) + Some(f64::E().powf(-0.2 * f64::from(score))) } impl FromStr for CreatureRoleEnum { diff --git a/src/models/creature/creature_metadata/type_enum.rs b/src/models/creature/creature_metadata/type_enum.rs index f778fed..3236a27 100644 --- a/src/models/creature/creature_metadata/type_enum.rs +++ b/src/models/creature/creature_metadata/type_enum.rs @@ -50,7 +50,6 @@ impl FromStr for CreatureTypeEnum { type Err = (); fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { - "MONSTER" => Ok(CreatureTypeEnum::Monster), "NPC" => Ok(CreatureTypeEnum::Npc), _ => Ok(CreatureTypeEnum::Monster), } diff --git a/src/models/creature/creature_metadata/variant_enum.rs b/src/models/creature/creature_metadata/variant_enum.rs index 2d9f491..8c68703 100644 --- a/src/models/creature/creature_metadata/variant_enum.rs +++ b/src/models/creature/creature_metadata/variant_enum.rs @@ -5,7 +5,18 @@ use strum::Display; use utoipa::ToSchema; #[derive( - Serialize, Deserialize, ToSchema, Display, Eq, Hash, PartialEq, Ord, PartialOrd, Default, + Copy, + Clone, + Serialize, + Deserialize, + ToSchema, + Display, + Eq, + Hash, + PartialEq, + Ord, + PartialOrd, + Default, )] pub enum CreatureVariant { Weak, @@ -14,18 +25,8 @@ pub enum CreatureVariant { Base, } -impl Clone for CreatureVariant { - fn clone(&self) -> CreatureVariant { - match self { - CreatureVariant::Elite => CreatureVariant::Elite, - CreatureVariant::Weak => CreatureVariant::Weak, - CreatureVariant::Base => CreatureVariant::Base, - } - } -} - impl CreatureVariant { - pub fn to_adjustment_modifier(&self) -> i64 { + pub fn to_adjustment_modifier(self) -> i64 { match self { CreatureVariant::Weak => -2, CreatureVariant::Elite => 2, @@ -33,7 +34,7 @@ impl CreatureVariant { } } - pub fn get_variant_level(&self, base_lvl: i64) -> i64 { + pub fn get_variant_level(self, base_lvl: i64) -> i64 { match self { //Decrease the creature’s level by 1; if the creature is level 1, // instead decrease its level by 2. @@ -57,7 +58,7 @@ impl CreatureVariant { } } - pub fn get_variant_hp(&self, base_hp: i64, starting_lvl: i64) -> i64 { + pub fn get_variant_hp(self, base_hp: i64, starting_lvl: i64) -> i64 { let hp_mod_map = match self { CreatureVariant::Weak => hp_decrease_by_level(), CreatureVariant::Elite => hp_increase_by_level(), @@ -77,10 +78,10 @@ impl CreatureVariant { (base_hp + hp_mod).max(1) } - pub fn get_variant_archive_link(&self, archive_link: Option) -> Option { + pub fn get_variant_archive_link(self, archive_link: Option) -> Option { match self { CreatureVariant::Base => archive_link, - _ => add_boolean_query(&archive_link, &self.to_string(), true), + _ => add_boolean_query(Option::from(&archive_link), &self.to_string(), true), } } } diff --git a/src/models/creature/creature_struct.rs b/src/models/creature/creature_struct.rs index 6b66fd7..2d63267 100644 --- a/src/models/creature/creature_struct.rs +++ b/src/models/creature/creature_struct.rs @@ -24,22 +24,22 @@ impl Creature { /// Decrease the creature’s AC, attack modifiers, DCs, saving throws, Perception, and skill modifiers by 2. /// Decrease the damage of its Strikes and other offensive abilities by 2. If the creature has limits on how many times or how often it can use an ability (such as a spellcaster’s spells or a dragon’s breath), decrease the damage by 4 instead. pub fn convert_creature_to_variant(self, variant: CreatureVariant) -> Creature { - let mut cr = Self::from_core_with_variant(self.core_data, variant.clone()); + let mut cr = Self::from_core_with_variant(self.core_data, variant); cr.extra_data = self .extra_data - .map(|x| x.convert_from_base_to_variant(&variant)); + .map(|x| x.convert_from_base_to_variant(variant)); cr.combat_data = self .combat_data - .map(|x| x.convert_from_base_to_variant(&variant)); + .map(|x| x.convert_from_base_to_variant(variant)); cr.spell_caster_data = self .spell_caster_data - .map(|x| x.convert_from_base_to_variant(&variant)); + .map(|x| x.convert_from_base_to_variant(variant)); cr } pub fn convert_creature_to_pwl(self) -> Creature { let pwl_mod = if self.core_data.essential.base_level >= 0 { - self.core_data.essential.base_level as u64 + self.core_data.essential.base_level.unsigned_abs() } else { 0 }; diff --git a/src/models/encounter_structs.rs b/src/models/encounter_structs.rs index 862a3e8..646f9c6 100644 --- a/src/models/encounter_structs.rs +++ b/src/models/encounter_structs.rs @@ -149,6 +149,7 @@ pub enum AdventureGroupEnum { MookSquad, } +#[derive(Copy, Clone)] pub struct ExpRange { pub lower_bound: i64, pub upper_bound: i64, diff --git a/src/models/routers_validator_structs.rs b/src/models/routers_validator_structs.rs index 37ade05..ca55bf9 100644 --- a/src/models/routers_validator_structs.rs +++ b/src/models/routers_validator_structs.rs @@ -109,16 +109,17 @@ pub struct Dice { } impl Dice { - /// Dice roll will roll n dices with each roll in the range of 1<=result<=dice_size. - /// It returns the sum of n_of_dices rolls. + /// Dice roll will roll n dices with each roll in the range of 1<=result<=`dice_size`. + /// It returns the sum of `n_of_dices` rolls. /// IT SHOULD NEVER BE <1, OTHERWISE WE BREAK THE CONTRACT OF THE METHOD. - pub fn roll(&self) -> i64 { - let mut roll_result = 0; - for _ in 0..self.n_of_dices { + pub fn roll(&self) -> u16 { + let mut roll_result = 0_u16; + let n_of_dices = u16::from(self.n_of_dices); + for _ in 0..n_of_dices { // gen_range panics if n<2 (1..1), panic! // so we directly return 1 if that's the case - roll_result += if self.dice_size > 1 { - fastrand::i64(1..=self.dice_size as i64) + roll_result += if n_of_dices > 1 { + fastrand::u16(1..=n_of_dices) } else { 1 } @@ -133,8 +134,8 @@ impl Dice { // M = max value of the dice // N = number of dices // B = bonus dmg - let m = self.dice_size as f64; - let n = self.n_of_dices as f64; + let m = f64::from(self.dice_size); + let n = f64::from(self.n_of_dices); let b = bonus_dmg; let avg: f64 = (((m + 1.) / 2.) * n) + b; avg.floor() as i64 diff --git a/src/models/shop_structs.rs b/src/models/shop_structs.rs index 7a191e7..cae51b1 100644 --- a/src/models/shop_structs.rs +++ b/src/models/shop_structs.rs @@ -78,7 +78,7 @@ impl ShopTemplateEnum { pub fn get_allowed_item_types(&self) -> Vec { match self { - ShopTemplateEnum::Blacksmith => { + ShopTemplateEnum::Blacksmith | ShopTemplateEnum::General => { vec![ ItemTypeEnum::Armor, ItemTypeEnum::Shield, @@ -90,27 +90,14 @@ impl ShopTemplateEnum { ShopTemplateEnum::Alchemist => { vec![ItemTypeEnum::Consumable, ItemTypeEnum::Equipment] } - ShopTemplateEnum::General => { - vec![ - ItemTypeEnum::Armor, - ItemTypeEnum::Shield, - ItemTypeEnum::Weapon, - ItemTypeEnum::Consumable, - ItemTypeEnum::Equipment, - ] - } } } pub fn get_allowed_item_rarities(&self) -> Vec { match self { - ShopTemplateEnum::Blacksmith => { - vec![RarityEnum::Common, RarityEnum::Uncommon, RarityEnum::Rare] - } - ShopTemplateEnum::Alchemist => { - vec![RarityEnum::Common, RarityEnum::Uncommon, RarityEnum::Rare] - } - ShopTemplateEnum::General => { + ShopTemplateEnum::Blacksmith + | ShopTemplateEnum::Alchemist + | ShopTemplateEnum::General => { vec![RarityEnum::Common, RarityEnum::Uncommon, RarityEnum::Rare] } } @@ -119,7 +106,7 @@ impl ShopTemplateEnum { pub fn get_traits_whitelist(&self) -> Vec { // For future-proof, right now contains 0 logic match self { - ShopTemplateEnum::Blacksmith => { + ShopTemplateEnum::Blacksmith | ShopTemplateEnum::General => { vec![] } ShopTemplateEnum::Alchemist => { @@ -130,21 +117,14 @@ impl ShopTemplateEnum { "Potion".to_string(), ] } - ShopTemplateEnum::General => { - vec![] - } } } pub fn get_traits_blacklist(&self) -> Vec { match self { - ShopTemplateEnum::Blacksmith => { - vec![] - } - ShopTemplateEnum::Alchemist => { - vec![] - } - ShopTemplateEnum::General => { + ShopTemplateEnum::Blacksmith + | ShopTemplateEnum::Alchemist + | ShopTemplateEnum::General => { vec![] } } diff --git a/src/routes/bestiary.rs b/src/routes/bestiary.rs index 8a0ff22..2cf142a 100644 --- a/src/routes/bestiary.rs +++ b/src/routes/bestiary.rs @@ -319,7 +319,7 @@ pub async fn get_creature_types_list( )] #[get("/creature_roles")] pub async fn get_creature_roles_list() -> actix_web::Result { - Ok(web::Json(bestiary_service::get_creature_roles_list().await)) + Ok(web::Json(bestiary_service::get_creature_roles_list())) } #[utoipa::path( diff --git a/src/routes/encounter.rs b/src/routes/encounter.rs index 152411b..d2496c4 100644 --- a/src/routes/encounter.rs +++ b/src/routes/encounter.rs @@ -53,7 +53,7 @@ pub fn init_docs(doc: &mut utoipa::openapi::OpenApi) { pub async fn get_encounter_info( web::Json(body): web::Json, ) -> Result { - Ok(web::Json(encounter_service::get_encounter_info(body))) + Ok(web::Json(encounter_service::get_encounter_info(&body))) } #[utoipa::path( diff --git a/src/routes/shop.rs b/src/routes/shop.rs index da830e7..d96acc5 100644 --- a/src/routes/shop.rs +++ b/src/routes/shop.rs @@ -200,5 +200,5 @@ pub async fn get_items_traits_list(data: web::Data) -> actix_web::Resu )] #[get("/templates_data")] pub async fn get_templates_data() -> actix_web::Result { - Ok(web::Json(shop_service::get_shop_templates_data().await)) + Ok(web::Json(shop_service::get_shop_templates_data())) } diff --git a/src/services/bestiary_service.rs b/src/services/bestiary_service.rs index 566fffd..a1b8282 100644 --- a/src/services/bestiary_service.rs +++ b/src/services/bestiary_service.rs @@ -6,7 +6,7 @@ use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; use crate::models::creature::creature_struct::Creature; use crate::models::response_data::{ResponseCreature, ResponseDataModifiers}; use crate::models::routers_validator_structs::CreatureFieldFilters; -use crate::services::url_calculator::bestiary_next_url_calculator; +use crate::services::url_calculator::bestiary_next_url; use crate::AppState; use anyhow::Result; use serde::{Deserialize, Serialize}; @@ -95,7 +95,7 @@ pub async fn get_creature_types_list(app_state: &AppState) -> Vec { .await } -pub async fn get_creature_roles_list() -> Vec { +pub fn get_creature_roles_list() -> Vec { CreatureRoleEnum::list() } fn convert_result_to_bestiary_response( @@ -110,8 +110,9 @@ fn convert_result_to_bestiary_response( BestiaryResponse { results: Some(cr.into_iter().map(ResponseCreature::from).collect()), count: cr_length, - next: if cr_length >= pagination.paginated_request.page_size as usize { - Some(bestiary_next_url_calculator( + next: if cr_length >= pagination.paginated_request.page_size.unsigned_abs() as usize + { + Some(bestiary_next_url( field_filters, pagination, cr_length as u32, diff --git a/src/services/encounter_handler/difficulty_utilities.rs b/src/services/encounter_handler/difficulty_utilities.rs index dab2df6..fc69be6 100644 --- a/src/services/encounter_handler/difficulty_utilities.rs +++ b/src/services/encounter_handler/difficulty_utilities.rs @@ -45,7 +45,8 @@ fn get_next_difficulty(diff: &EncounterChallengeEnum) -> EncounterChallengeEnum EncounterChallengeEnum::Low => EncounterChallengeEnum::Moderate, EncounterChallengeEnum::Moderate => EncounterChallengeEnum::Severe, EncounterChallengeEnum::Severe => EncounterChallengeEnum::Extreme, - EncounterChallengeEnum::Extreme => EncounterChallengeEnum::Impossible, - EncounterChallengeEnum::Impossible => EncounterChallengeEnum::Impossible, + EncounterChallengeEnum::Extreme | EncounterChallengeEnum::Impossible => { + EncounterChallengeEnum::Impossible + } } } diff --git a/src/services/encounter_handler/encounter_calculator.rs b/src/services/encounter_handler/encounter_calculator.rs index da256d9..8987577 100644 --- a/src/services/encounter_handler/encounter_calculator.rs +++ b/src/services/encounter_handler/encounter_calculator.rs @@ -78,7 +78,8 @@ pub fn calculate_encounter_scaling_difficulty( for curr_diff in EncounterChallengeEnum::iter() { diff_scaled_exp_map.insert( curr_diff.clone(), - scale_difficulty_exp(&curr_diff, party_size as i64).lower_bound, + scale_difficulty_exp(&curr_diff, i64::try_from(party_size).unwrap_or(i64::MAX)) + .lower_bound, ); } diff_scaled_exp_map @@ -125,12 +126,15 @@ pub fn calculate_lvl_combination_for_encounter( is_pwl_on: bool, ) -> HashSet> { // Given an encounter difficulty it calculates all possible encounter permutations - let exp_range = scale_difficulty_exp(difficulty, party_levels.len() as i64); + let exp_range = scale_difficulty_exp( + difficulty, + i64::try_from(party_levels.len()).unwrap_or(i64::MAX), + ); let party_avg: f64 = party_levels.iter().sum::() as f64 / party_levels.len() as f64; calculate_lvl_combinations_for_given_exp( exp_range, party_avg.floor() as i64, - calculate_lvl_and_exp_map(is_pwl_on), + &calculate_lvl_and_exp_map(is_pwl_on), ) } @@ -139,8 +143,8 @@ pub fn filter_combinations_outside_range( lower_bound: Option, upper_bound: Option, ) -> HashSet> { - let mut lower = lower_bound.unwrap_or(0) as i64; - let mut upper = upper_bound.unwrap_or(0) as i64; + let mut lower = i64::from(lower_bound.unwrap_or(0)); + let mut upper = i64::from(upper_bound.unwrap_or(0)); if lower != 0 && upper == 0 { upper = lower; } else if lower == 0 && upper != 0 { @@ -148,13 +152,13 @@ pub fn filter_combinations_outside_range( } else if lower == 0 && upper == 0 { return combinations; } - let mut filtered_combo = HashSet::new(); - combinations.iter().for_each(|curr_combo| { - if curr_combo.len() >= lower as usize && curr_combo.len() <= upper as usize { - filtered_combo.insert(curr_combo.clone()); - } - }); - filtered_combo + combinations + .into_iter() + .filter(|curr_combo| { + curr_combo.len() >= lower.unsigned_abs() as usize + && curr_combo.len() <= upper.unsigned_abs() as usize + }) + .collect::>>() } fn convert_lvl_diff_into_exp( @@ -163,34 +167,35 @@ fn convert_lvl_diff_into_exp( lvl_and_exp_map: &HashMap, ) -> i64 { let lvl_diff_rounded_down = lvl_diff.floor() as i64; - lvl_and_exp_map - .get(&lvl_diff_rounded_down) - .map(|value| value.abs()) - .unwrap_or( - if lvl_diff_rounded_down < calculate_max_lvl_diff(lvl_and_exp_map) { - 0 - } else { - // To avoid the party of 50 level 1 pg destroying a lvl 20 - scale_difficulty_exp(&EncounterChallengeEnum::Impossible, party_size as i64) - .lower_bound - }, - ) + lvl_and_exp_map.get(&lvl_diff_rounded_down).map_or( + if lvl_diff_rounded_down < calculate_max_lvl_diff(lvl_and_exp_map) { + 0 + } else { + // To avoid the party of 50 level 1 pg destroying a lvl 20 + scale_difficulty_exp( + &EncounterChallengeEnum::Impossible, + i64::try_from(party_size).unwrap_or(i64::MAX), + ) + .lower_bound + }, + |value| value.abs(), + ) } fn calculate_lvl_combinations_for_given_exp( experience_range: ExpRange, party_lvl: i64, - lvl_and_exp_map: HashMap, + lvl_and_exp_map: &HashMap, ) -> HashSet> { // Given an encounter experience it calculates all possible encounter permutations - let exp_list = lvl_and_exp_map.values().copied().collect(); - find_combinations(exp_list, experience_range) + let exp_list = lvl_and_exp_map.values().copied().collect::>(); + find_combinations(&exp_list, experience_range) .iter() .map(|curr_combination| { curr_combination .iter() - .map(|curr_exp| convert_exp_to_lvl_diff(*curr_exp, &lvl_and_exp_map)) - .filter(|a| a.is_some()) + .map(|curr_exp| convert_exp_to_lvl_diff(*curr_exp, lvl_and_exp_map)) + .filter(Option::is_some) .map(|lvl_diff| party_lvl + lvl_diff.unwrap()) .collect() }) @@ -206,11 +211,11 @@ fn convert_exp_to_lvl_diff(experience: i64, lvl_and_exp_map: &HashMap) .find_map(|(key, &exp)| if exp == experience { Some(*key) } else { None }) } -fn find_combinations(candidates: Vec, target_range: ExpRange) -> Vec> { +fn find_combinations(candidates: &[i64], target_range: ExpRange) -> Vec> { // Find all the combination of numbers in the candidates vector // that sums up to the target. I.e coin changing problem fn backtrack( - candidates: &Vec, + candidates: &[i64], lb_target: i64, ub_target: i64, start: usize, @@ -250,7 +255,7 @@ fn find_combinations(candidates: Vec, target_range: ExpRange) -> Vec EncounterInfoResponse { +pub fn get_encounter_info(enc_params: &EncounterParams) -> EncounterInfoResponse { let enc_exp = encounter_calculator::calculate_encounter_exp( &enc_params.party_levels, &enc_params.enemy_levels, @@ -67,8 +68,8 @@ pub async fn generate_random_encounter( count: 0, encounter_info: EncounterInfoResponse { experience: 0, - challenge: Default::default(), - encounter_exp_levels: Default::default(), + challenge: EncounterChallengeEnum::default(), + encounter_exp_levels: BTreeMap::default(), }, } }) @@ -82,13 +83,12 @@ async fn calculate_random_encounter( ) -> Result { let is_pwl_on = enc_data.is_pwl_on; let filtered_lvl_combinations = get_lvl_combinations(&enc_data, &party_levels); - let unique_levels = HashSet::from_iter( - filtered_lvl_combinations - .clone() - .into_iter() - .flatten() - .map(|lvl| lvl.to_string()), - ); + let unique_levels = filtered_lvl_combinations + .clone() + .into_iter() + .flatten() + .map(|lvl| lvl.to_string()) + .collect::>(); ensure!( !unique_levels.is_empty(), "There are no valid levels to chose from. Encounter could not be built" @@ -118,7 +118,7 @@ async fn calculate_random_encounter( "No creatures have been fetched" ); let chosen_encounter = - choose_random_creatures_combination(filtered_creatures, filtered_lvl_combinations)?; + choose_random_creatures_combination(&filtered_creatures, filtered_lvl_combinations)?; Ok(RandomEncounterGeneratorResponse { count: chosen_encounter.len(), @@ -129,7 +129,7 @@ async fn calculate_random_encounter( .map(ResponseCreature::from) .collect(), ), - encounter_info: get_encounter_info(EncounterParams { + encounter_info: get_encounter_info(&EncounterParams { party_levels, enemy_levels: chosen_encounter .iter() @@ -141,7 +141,7 @@ async fn calculate_random_encounter( } fn choose_random_creatures_combination( - filtered_creatures: Vec, + filtered_creatures: &[Creature], lvl_combinations: HashSet>, ) -> Result> { // Chooses an id combination, could be (1, 1, 2). Admits duplicates @@ -150,8 +150,8 @@ fn choose_random_creatures_combination( creatures_ordered_by_level .keys() .for_each(|key| list_of_levels.push(*key)); - let existing_levels = filter_non_existing_levels(list_of_levels, lvl_combinations); - let tmp = Vec::from_iter(existing_levels.iter()); + let existing_levels = filter_non_existing_levels(&list_of_levels, lvl_combinations); + let tmp = existing_levels.iter().collect::>(); ensure!( !tmp.is_empty(), "No valid level combinations to randomly choose from" @@ -177,7 +177,7 @@ fn choose_random_creatures_combination( filled_vec_of_creatures.iter(), required_number_of_creatures_with_level, ) { - result_vec.push(curr_chosen_creature.clone()) + result_vec.push(curr_chosen_creature.clone()); } } Ok(result_vec) @@ -210,7 +210,7 @@ fn fill_vector_if_it_does_not_contain_enough_elements( } fn filter_non_existing_levels( - creatures_levels: Vec, + creatures_levels: &[i64], level_combinations: HashSet>, ) -> HashSet> { // Removes the vec with levels that are not found in any creature @@ -238,31 +238,41 @@ fn build_filter_map(filter_enum: FilterStruct) -> HashMap>(), ); }; if let Some(vec) = filter_enum.sizes { filter_map.insert( CreatureFilter::Size, - HashSet::from_iter(vec.iter().map(|el| el.to_string())), + vec.iter() + .map(std::string::ToString::to_string) + .collect::>(), ); }; if let Some(vec) = filter_enum.alignments { filter_map.insert( CreatureFilter::Alignment, - HashSet::from_iter(vec.iter().map(|x| x.to_string())), + vec.iter() + .map(std::string::ToString::to_string) + .collect::>(), ); }; if let Some(vec) = filter_enum.creature_types { filter_map.insert( CreatureFilter::CreatureTypes, - HashSet::from_iter(vec.iter().map(|el| el.to_string())), + vec.iter() + .map(std::string::ToString::to_string) + .collect::>(), ); }; if let Some(vec) = filter_enum.creature_roles { filter_map.insert( CreatureFilter::CreatureRoles, - HashSet::from_iter(vec.iter().map(|el| el.to_db_column())), + vec.iter() + .map(CreatureRoleEnum::to_db_column) + .collect::>(), ); }; filter_map.insert(CreatureFilter::Level, filter_enum.lvl_combinations); @@ -314,7 +324,8 @@ fn get_adventure_group_lvl_combinations( adv_group: &AdventureGroupEnum, party_levels: &[i64], ) -> HashSet> { - let party_avg = party_levels.iter().sum::() / party_levels.len() as i64; + let party_avg = + party_levels.iter().sum::() / i64::try_from(party_levels.len()).unwrap_or(i64::MAX); let mut result = HashSet::new(); result.insert(match adv_group { AdventureGroupEnum::BossAndLackeys => { diff --git a/src/services/shop_service.rs b/src/services/shop_service.rs index 92ab770..1a929c6 100644 --- a/src/services/shop_service.rs +++ b/src/services/shop_service.rs @@ -1,11 +1,11 @@ use crate::db::shop_proxy; use crate::models::response_data::ResponseItem; -use crate::models::routers_validator_structs::ItemFieldFilters; +use crate::models::routers_validator_structs::{Dice, ItemFieldFilters}; use crate::models::shop_structs::{ ItemTableFieldsFilter, RandomShopData, ShopFilterQuery, ShopPaginatedRequest, ShopTemplateData, ShopTemplateEnum, }; -use crate::services::url_calculator::shop_next_url_calculator; +use crate::services::url_calculator::shop_next_url; use crate::AppState; use anyhow::{bail, Context}; use num_traits::ToPrimitive; @@ -54,8 +54,14 @@ pub async fn generate_random_shop_listing( ) }; let shop_type = shop_data.shop_template.clone().unwrap_or_default(); - let n_of_consumables: i64 = shop_data.consumable_dices.iter().map(|x| x.roll()).sum(); - let n_of_equippables: i64 = shop_data.equippable_dices.iter().map(|x| x.roll()).sum(); + let n_of_consumables = i64::from( + shop_data + .consumable_dices + .iter() + .map(Dice::roll) + .sum::(), + ); + let n_of_equippables = shop_data.equippable_dices.iter().map(Dice::roll).sum(); // The request is correct, but will result in an empty list. if n_of_consumables == 0 && n_of_equippables == 0 { return ShopListingResponse::default(); @@ -143,8 +149,10 @@ pub async fn get_traits_list(app_state: &AppState) -> Vec { shop_proxy::get_all_traits(app_state).await } -pub async fn get_shop_templates_data() -> Vec { - ShopTemplateEnum::iter().map(|x| x.into()).collect() +pub fn get_shop_templates_data() -> Vec { + ShopTemplateEnum::iter() + .map(std::convert::Into::into) + .collect() } fn convert_result_to_shop_response( @@ -159,12 +167,10 @@ fn convert_result_to_shop_response( ShopListingResponse { results: Some(item), count: n_of_items, - next: if n_of_items >= pagination.paginated_request.page_size as usize { - Some(shop_next_url_calculator( - field_filters, - pagination, - n_of_items as u32, - )) + next: if n_of_items + >= pagination.paginated_request.page_size.unsigned_abs() as usize + { + Some(shop_next_url(field_filters, pagination, n_of_items as u32)) } else { None }, @@ -178,24 +184,24 @@ fn convert_result_to_shop_response( /// Gets the n of: equipment, weapons, armors, shields (in this order). /// Changing order is considered a BREAKING CHANGE. pub fn calculate_n_of_equippable_values( - n_of_equippables: i64, + n_of_equippables: u16, percentages: (u8, u8, u8, u8), ) -> anyhow::Result<(i64, i64, i64, i64)> { let (e_p, w_p, a_p, s_p) = percentages; - let sum_of_percentages = (e_p + w_p + a_p + s_p) as f64; + let sum_of_percentages = f64::from(e_p + w_p + a_p + s_p); if sum_of_percentages > 100. { bail!("Percentages sum value is higher than 100. Incorrect values.") } - let f_n_of_equippables = n_of_equippables as f64; + let f_n_of_equippables = f64::from(n_of_equippables); let (e_v, w_v, a_v, s_v) = if sum_of_percentages == 0. { (25., 25., 25., 25.) } else { ( //Simpler form: (f_n_of_equippables * ((w_p as f64 * 100.) / sum_of_percentages)) / 100., - (f_n_of_equippables * e_p as f64) / sum_of_percentages, - (f_n_of_equippables * w_p as f64) / sum_of_percentages, - (f_n_of_equippables * a_p as f64) / sum_of_percentages, - (f_n_of_equippables * s_p as f64) / sum_of_percentages, + (f_n_of_equippables * f64::from(e_p)) / sum_of_percentages, + (f_n_of_equippables * f64::from(w_p)) / sum_of_percentages, + (f_n_of_equippables * f64::from(a_p)) / sum_of_percentages, + (f_n_of_equippables * f64::from(s_p)) / sum_of_percentages, ) }; @@ -216,7 +222,7 @@ mod tests { #[case(10, (10,10,10,10), (3,3,3,3))] #[case(1, (10,10,10,10), (1,1,1,1))] fn calculate_equippable_values_rounded_over_desired_total_case( - #[case] input_n_of_equippables: i64, + #[case] input_n_of_equippables: u16, #[case] input_percentages: (u8, u8, u8, u8), #[case] expected: (i64, i64, i64, i64), ) { @@ -228,7 +234,7 @@ mod tests { #[rstest] #[case(0, (0,0,0,0), (25, 25, 25, 25))] fn calculate_equippable_values_with_all_0( - #[case] input_n_of_equippables: i64, + #[case] input_n_of_equippables: u16, #[case] input_percentages: (u8, u8, u8, u8), #[case] expected: (i64, i64, i64, i64), ) { @@ -241,7 +247,7 @@ mod tests { #[case(0, (10,10,10,10), (0,0,0,0))] #[case(0, (10,20,10,0), (0,0,0,0))] fn calculate_equippable_values_zero_as_n_of_equippables( - #[case] input_n_of_equippables: i64, + #[case] input_n_of_equippables: u16, #[case] input_percentages: (u8, u8, u8, u8), #[case] expected: (i64, i64, i64, i64), ) { @@ -255,7 +261,7 @@ mod tests { #[case(10, (10,10,0,0), (5,5,0,0))] #[case(10, (10,10,10,0), (4,4,4,0))] fn calculate_equippable_values_with_missing_percentages( - #[case] input_n_of_equippables: i64, + #[case] input_n_of_equippables: u16, #[case] input_percentages: (u8, u8, u8, u8), #[case] expected: (i64, i64, i64, i64), ) { diff --git a/src/services/url_calculator.rs b/src/services/url_calculator.rs index 20af47b..db46249 100644 --- a/src/services/url_calculator.rs +++ b/src/services/url_calculator.rs @@ -2,7 +2,7 @@ use crate::models::bestiary_structs::BestiaryPaginatedRequest; use crate::models::routers_validator_structs::{CreatureFieldFilters, ItemFieldFilters}; use crate::models::shop_structs::ShopPaginatedRequest; -pub fn shop_next_url_calculator( +pub fn shop_next_url( field_filters: &ItemFieldFilters, pagination: &ShopPaginatedRequest, next_cursor: u32, @@ -25,10 +25,10 @@ pub fn shop_next_url_calculator( .clone() .unwrap_or_default() ); - format!("{}{}{}", base_url, filter_query, pagination_query) + format!("{base_url}{filter_query}{pagination_query}") } -pub fn bestiary_next_url_calculator( +pub fn bestiary_next_url( field_filters: &CreatureFieldFilters, pagination: &BestiaryPaginatedRequest, next_cursor: u32, @@ -51,12 +51,12 @@ pub fn bestiary_next_url_calculator( .clone() .unwrap_or_default() ); - format!("{}{}{}", base_url, filter_query, pagination_query) + format!("{base_url}{filter_query}{pagination_query}") } -pub fn add_boolean_query(url: &Option, key: &String, value: bool) -> Option { +pub fn add_boolean_query(url: Option<&String>, key: &String, value: bool) -> Option { url.as_ref() - .map(|base_url| format!("{}&{}={}", base_url, key, value)) + .map(|base_url| format!("{base_url}&{key}={value}")) } fn creature_filter_query_calculator(field_filters: &CreatureFieldFilters) -> String { @@ -64,34 +64,34 @@ fn creature_filter_query_calculator(field_filters: &CreatureFieldFilters) -> Str field_filters .name_filter .clone() - .map(|name| format!("name_filter={}", name)), + .map(|name| format!("name_filter={name}")), field_filters .role_threshold - .map(|threshold| format!("role_threshold={}", threshold)), + .map(|threshold| format!("role_threshold={threshold}")), field_filters .min_hp_filter - .map(|hp| format!("min_hp_filter={}", hp)), + .map(|hp| format!("min_hp_filter={hp}")), field_filters .max_hp_filter - .map(|hp| format!("max_hp_filter={}", hp)), + .map(|hp| format!("max_hp_filter={hp}")), field_filters .min_level_filter - .map(|lvl| format!("min_level_filter={}", lvl)), + .map(|lvl| format!("min_level_filter={lvl}")), field_filters .max_level_filter - .map(|lvl| format!("max_level_filter={}", lvl)), + .map(|lvl| format!("max_level_filter={lvl}")), field_filters .is_melee_filter - .map(|is| format!("is_melee_filter={}", is)), + .map(|is| format!("is_melee_filter={is}")), field_filters .is_ranged_filter - .map(|is| format!("is_ranged_filter={}", is)), + .map(|is| format!("is_ranged_filter={is}")), field_filters .is_spell_caster_filter - .map(|is| format!("is_spell_caster_filter={}", is)), + .map(|is| format!("is_spell_caster_filter={is}")), ] .iter() - .filter_map(|opt| opt.clone()) + .filter_map(std::clone::Clone::clone) .collect(); match queries.len() { 0 => String::new(), @@ -103,43 +103,43 @@ fn shop_filter_query_calculator(field_filters: &ItemFieldFilters) -> String { let queries: Vec = [ field_filters .min_bulk_filter - .map(|bulk| format!("min_bulk_filter={}", bulk)), + .map(|bulk| format!("min_bulk_filter={bulk}")), field_filters .max_bulk_filter - .map(|bulk| format!("max_bulk_filter={}", bulk)), + .map(|bulk| format!("max_bulk_filter={bulk}")), field_filters .min_hardness_filter - .map(|hn| format!("min_hardness_filter={}", hn)), + .map(|hn| format!("min_hardness_filter={hn}")), field_filters .max_hardness_filter - .map(|hn| format!("max_hardness_filter={}", hn)), + .map(|hn| format!("max_hardness_filter={hn}")), field_filters .min_hp_filter - .map(|hp| format!("min_hp_filter={}", hp)), + .map(|hp| format!("min_hp_filter={hp}")), field_filters .max_hp_filter - .map(|hp| format!("max_hp_filter={}", hp)), + .map(|hp| format!("max_hp_filter={hp}")), field_filters .min_level_filter - .map(|lvl| format!("min_level_filter={}", lvl)), + .map(|lvl| format!("min_level_filter={lvl}")), field_filters .max_level_filter - .map(|lvl| format!("max_level_filter={}", lvl)), + .map(|lvl| format!("max_level_filter={lvl}")), field_filters .min_price_filter - .map(|price| format!("min_price_filter={}", price)), + .map(|price| format!("min_price_filter={price}")), field_filters .max_price_filter - .map(|price| format!("max_price_filter={}", price)), + .map(|price| format!("max_price_filter={price}")), field_filters .min_n_of_uses_filter - .map(|uses| format!("min_n_of_uses_filter={}", uses)), + .map(|uses| format!("min_n_of_uses_filter={uses}")), field_filters .max_n_of_uses_filter - .map(|uses| format!("max_n_of_uses_filter={}", uses)), + .map(|uses| format!("max_n_of_uses_filter={uses}")), ] .iter() - .filter_map(|opt| opt.clone()) + .filter_map(std::clone::Clone::clone) .collect(); match queries.len() { 0 => String::new(), From 2057efd7dff793c2edbc418b7efd6af3d8781b01 Mon Sep 17 00:00:00 2001 From: rakuja Date: Sun, 8 Dec 2024 11:16:33 +0100 Subject: [PATCH 07/18] refactor: add nursery rules --- Cargo.toml | 4 +- src/db/bestiary_proxy.rs | 3 +- src/db/cr_core_initializer.rs | 2 +- src/db/data_providers/creature_fetcher.rs | 28 +++----- src/db/data_providers/shop_fetcher.rs | 15 +--- src/db/shop_proxy.rs | 23 +++--- src/lib.rs | 22 +++--- .../creature_component/creature_combat.rs | 8 +-- .../creature_component/creature_core.rs | 8 +-- .../creature_component/creature_extra.rs | 6 +- .../creature_spell_caster.rs | 6 +- src/models/creature/creature_filter_enum.rs | 26 +++---- .../creature_metadata/alignment_enum.rs | 72 +++++++++---------- .../creature_metadata/creature_role.rs | 54 +++++++------- .../creature/creature_metadata/type_enum.rs | 16 ++--- .../creature_metadata/variant_enum.rs | 26 +++---- src/models/creature/creature_struct.rs | 10 +-- src/models/encounter_structs.rs | 18 ++--- src/models/item/armor_struct.rs | 2 +- src/models/item/item_metadata/type_enum.rs | 60 ++++++++-------- src/models/item/item_struct.rs | 2 +- src/models/item/shield_struct.rs | 2 +- src/models/item/weapon_struct.rs | 10 +-- src/models/pf_version_enum.rs | 6 +- src/models/response_data.rs | 2 +- src/models/routers_validator_structs.rs | 14 ++-- src/models/shared/rarity_enum.rs | 20 +++--- src/models/shared/size_enum.rs | 28 ++++---- src/models/shop_structs.rs | 36 ++++------ .../encounter_handler/difficulty_utilities.rs | 11 +-- .../encounter_handler/encounter_calculator.rs | 30 ++++---- src/services/encounter_service.rs | 11 ++- src/services/shop_service.rs | 45 ++++++------ 33 files changed, 298 insertions(+), 328 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d72cba3..b2af5de 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -37,9 +37,11 @@ module_name_repetitions = {level = "allow", priority = 2} cast_possible_truncation = {level = "allow", priority = 2} cast_precision_loss = {level = "allow", priority = 2} +future_not_send = {level = "allow", priority = 2} + pedantic = {level = "warn", priority = 1} #restriction = "warn" -# nursery = "warn" +nursery = {level= "warn", priority = 1} [dependencies] diff --git a/src/db/bestiary_proxy.rs b/src/db/bestiary_proxy.rs index 2464f31..dfa07b2 100644 --- a/src/db/bestiary_proxy.rs +++ b/src/db/bestiary_proxy.rs @@ -125,8 +125,7 @@ pub async fn get_creatures_passing_all_filters( let mut creature_vec = Vec::new(); let level_vec = key_value_filters .get(&CreatureFilter::Level) - .unwrap_or(&HashSet::new()) - .clone(); + .map_or_else(HashSet::new, std::clone::Clone::clone); let modified_filters = prepare_filters_for_db_communication(key_value_filters, fetch_weak, fetch_elite); for core in diff --git a/src/db/cr_core_initializer.rs b/src/db/cr_core_initializer.rs index a4c5c60..690ddb1 100644 --- a/src/db/cr_core_initializer.rs +++ b/src/db/cr_core_initializer.rs @@ -29,7 +29,7 @@ pub async fn update_creature_core_table(conn: &Pool) -> Result<()> { hp: cr.hp, base_level: cr.level, size: cr.size, - family: cr.family.unwrap_or(String::from("-")), + family: cr.family.unwrap_or_else(|| String::from("-")), rarity: cr.rarity, license: cr.license, remaster: cr.remaster, diff --git a/src/db/data_providers/creature_fetcher.rs b/src/db/data_providers/creature_fetcher.rs index 2b60bf4..017faa5 100644 --- a/src/db/data_providers/creature_fetcher.rs +++ b/src/db/data_providers/creature_fetcher.rs @@ -334,7 +334,7 @@ async fn fetch_creature_items(conn: &Pool, creature_id: i64) -> Result, creature_id: i64, item_id: i64) -> i64 { - match sqlx::query!( + sqlx::query!( "SELECT quantity FROM ITEM_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1) AND item_id == ($2)", creature_id, @@ -342,17 +342,14 @@ async fn fetch_item_quantity(conn: &Pool, creature_id: i64, item_id: i64 ) .fetch_one(conn) .await - { - Ok(r) => r.quantity, - Err(_) => 1, - } + .map_or(1, |q| q.quantity) } /// Quantities are present ONLY for creature's weapons. /// It needs to be fetched from the association table. /// It defaults to 1 if error are found async fn fetch_weapon_quantity(conn: &Pool, creature_id: i64, weapon_id: i64) -> i64 { - match sqlx::query!( + sqlx::query!( "SELECT quantity FROM WEAPON_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1) AND weapon_id == ($2)", creature_id, @@ -360,17 +357,14 @@ async fn fetch_weapon_quantity(conn: &Pool, creature_id: i64, weapon_id: ) .fetch_one(conn) .await - { - Ok(r) => r.quantity, - Err(_) => 1, - } + .map_or(1, |r| r.quantity) } /// Quantities are present ONLY for creature's shields. /// It needs to be fetched from the association table. /// It defaults to 1 if error are found async fn fetch_shield_quantity(conn: &Pool, creature_id: i64, shield_id: i64) -> i64 { - match sqlx::query!( + sqlx::query!( "SELECT quantity FROM SHIELD_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1) AND shield_id == ($2)", creature_id, @@ -378,17 +372,14 @@ async fn fetch_shield_quantity(conn: &Pool, creature_id: i64, shield_id: ) .fetch_one(conn) .await - { - Ok(r) => r.quantity, - Err(_) => 1, - } + .map_or(1, |r| r.quantity) } /// Quantities are present ONLY for creature's armors. /// It needs to be fetched from the association table. /// It defaults to 1 if error are found async fn fetch_armor_quantity(conn: &Pool, creature_id: i64, armor_id: i64) -> i64 { - match sqlx::query!( + sqlx::query!( "SELECT quantity FROM ARMOR_CREATURE_ASSOCIATION_TABLE WHERE creature_id == ($1) AND armor_id == ($2)", creature_id, @@ -396,10 +387,7 @@ async fn fetch_armor_quantity(conn: &Pool, creature_id: i64, armor_id: i ) .fetch_one(conn) .await - { - Ok(r) => r.quantity, - Err(_) => 1, - } + .map_or(1, |r| r.quantity) } async fn fetch_creature_actions(conn: &Pool, creature_id: i64) -> Result> { diff --git a/src/db/data_providers/shop_fetcher.rs b/src/db/data_providers/shop_fetcher.rs index 3df257c..17f0166 100644 --- a/src/db/data_providers/shop_fetcher.rs +++ b/src/db/data_providers/shop_fetcher.rs @@ -157,10 +157,7 @@ pub async fn fetch_weapons( el.item_core.traits = fetch_item_traits(conn, el.item_core.id).await?; el.weapon_data.property_runes = fetch_weapon_runes(conn, el.weapon_data.id).await?; el.weapon_data.damage_data = fetch_weapon_damage_data(conn, el.weapon_data.id).await?; - result_vec.push(Weapon { - item_core: el.item_core, - weapon_data: el.weapon_data, - }); + result_vec.push(el); } Ok(result_vec) } @@ -187,10 +184,7 @@ pub async fn fetch_armors(conn: &Pool, cursor: u32, page_size: i16) -> R for mut el in x { el.item_core.traits = fetch_item_traits(conn, el.item_core.id).await?; el.armor_data.property_runes = fetch_armor_runes(conn, el.armor_data.id).await?; - result_vec.push(Armor { - item_core: el.item_core, - armor_data: el.armor_data, - }); + result_vec.push(el); } Ok(result_vec) } @@ -219,10 +213,7 @@ pub async fn fetch_shields( let mut result_vec = Vec::new(); for mut el in x { el.item_core.traits = fetch_item_traits(conn, el.item_core.id).await?; - result_vec.push(Shield { - item_core: el.item_core, - shield_data: el.shield_data, - }); + result_vec.push(el); } Ok(result_vec) } diff --git a/src/db/shop_proxy.rs b/src/db/shop_proxy.rs index 224d18e..2291c57 100644 --- a/src/db/shop_proxy.rs +++ b/src/db/shop_proxy.rs @@ -136,18 +136,17 @@ async fn get_list(app_state: &AppState) -> Vec { /// Gets all the runtime sources. It will cache the result #[once(sync_writes = true)] pub async fn get_all_sources(app_state: &AppState) -> Vec { - match get_all_items_from_db(app_state).await { - Ok(v) => v - .into_iter() - .map(|x| x.source) - .unique() - .filter(|x| !x.is_empty()) - .sorted() - .collect(), - Err(_) => { - vec![] - } - } + get_all_items_from_db(app_state).await.map_or_else( + |_| vec![], + |v| { + v.into_iter() + .map(|x| x.source) + .unique() + .filter(|x| !x.is_empty()) + .sorted() + .collect() + }, + ) } /// Gets all the runtime traits. It will cache the result diff --git a/src/lib.rs b/src/lib.rs index 819f0f8..6e597da 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -69,19 +69,19 @@ fn get_service_startup_state() -> StartupState { } fn get_service_port() -> u16 { - match env::var("SERVICE_PORT").ok() { - None => 25566, - Some(port) => port.parse::().unwrap_or(25566), - } + env::var("SERVICE_PORT") + .ok() + .map_or(25566, |port| port.parse().unwrap_or(25566)) } fn get_service_workers() -> usize { let available_cpus = usize::from(std::thread::available_parallelism().unwrap_or(NonZero::new(1).unwrap())); - match env::var("N_OF_SERVICE_WORKERS").ok() { - None => available_cpus, - Some(n_of_workers) => n_of_workers.parse::().unwrap_or(available_cpus), - } + env::var("N_OF_SERVICE_WORKERS") + .ok() + .map_or(available_cpus, |n_of_workers| { + n_of_workers.parse().unwrap_or(available_cpus) + }) } fn init_docs(openapi: &mut utoipa::openapi::OpenApi) { @@ -108,11 +108,7 @@ pub async fn start( } InitializeLogResponsibility::Delegated => {} // do nothing, someone else has already initialized them } - let db_url = if let Some(x) = db_location { - x - } else { - get_service_db_url() - }; + let db_url = db_location.map_or_else(get_service_db_url, |x| x); let service_ip = get_service_ip(); let service_port = get_service_port(); let startup_state: StartupState = get_service_startup_state(); diff --git a/src/models/creature/creature_component/creature_combat.rs b/src/models/creature/creature_component/creature_combat.rs index abb56b8..a396583 100644 --- a/src/models/creature/creature_component/creature_combat.rs +++ b/src/models/creature/creature_component/creature_combat.rs @@ -38,7 +38,7 @@ pub struct CreatureCombatData { } impl CreatureCombatData { - fn add_mod_to_saving_throws_and_ac_and_wp_to_hit(self, modifier: i64) -> CreatureCombatData { + fn add_mod_to_saving_throws_and_ac_and_wp_to_hit(self, modifier: i64) -> Self { let mut com_data = self; let weapons: Vec = com_data .weapons @@ -57,7 +57,7 @@ impl CreatureCombatData { com_data } - fn add_mod_to_dmg(self, modifier: i64) -> CreatureCombatData { + fn add_mod_to_dmg(self, modifier: i64) -> Self { let mut com_data = self; let weapons: Vec = com_data .weapons @@ -83,7 +83,7 @@ impl CreatureCombatData { } /// Lowers saving throws, weapon to hit bonus, and ac by the given `pwl_mod` - pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureCombatData { + pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> Self { self.add_mod_to_saving_throws_and_ac_and_wp_to_hit( -i64::try_from(pwl_mod).unwrap_or(i64::MAX), ) @@ -93,7 +93,7 @@ impl CreatureCombatData { /// If the creature has limits on how many times or how often it can use an ability /// (such as a spellcaster’s spells or a dragon’s breath), decrease the damage by 4 instead. /// Increase/Decrease the creature’s AC, attack modifiers, DCs, saving throws by 2. - pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> CreatureCombatData { + pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> Self { let modifier = variant.to_adjustment_modifier(); self.add_mod_to_saving_throws_and_ac_and_wp_to_hit(modifier) .add_mod_to_dmg(modifier) diff --git a/src/models/creature/creature_component/creature_core.rs b/src/models/creature/creature_component/creature_core.rs index 04f6c97..6820b3a 100644 --- a/src/models/creature/creature_component/creature_core.rs +++ b/src/models/creature/creature_component/creature_core.rs @@ -61,14 +61,14 @@ impl<'r> FromRow<'r, SqliteRow> for EssentialData { let rarity: String = row.try_get("rarity")?; let size: String = row.try_get("size")?; let alignment: String = row.try_get("alignment")?; - Ok(EssentialData { + Ok(Self { id: row.try_get("id")?, aon_id: row.try_get("aon_id").ok(), name: row.try_get("name")?, hp: row.try_get("hp")?, base_level: row.try_get("level")?, size: SizeEnum::from(size), - family: row.try_get("family").unwrap_or(String::from("-")), + family: row.try_get("family").unwrap_or_else(|_| String::from("-")), rarity: RarityEnum::from(rarity), license: row.try_get("license")?, remaster: row.try_get("remaster")?, @@ -81,7 +81,7 @@ impl<'r> FromRow<'r, SqliteRow> for EssentialData { impl<'r> FromRow<'r, SqliteRow> for DerivedData { fn from_row(row: &'r SqliteRow) -> Result { - Ok(DerivedData { + Ok(Self { archive_link: row.try_get("archive_link").ok(), is_melee: row.try_get("is_melee")?, is_ranged: row.try_get("is_ranged")?, @@ -99,7 +99,7 @@ impl<'r> FromRow<'r, SqliteRow> for DerivedData { impl<'r> FromRow<'r, SqliteRow> for CreatureCoreData { fn from_row(row: &'r SqliteRow) -> Result { - Ok(CreatureCoreData { + Ok(Self { essential: EssentialData::from_row(row)?, derived: DerivedData::from_row(row)?, traits: vec![], diff --git a/src/models/creature/creature_component/creature_extra.rs b/src/models/creature/creature_component/creature_extra.rs index e3197c8..9301db3 100644 --- a/src/models/creature/creature_component/creature_extra.rs +++ b/src/models/creature/creature_component/creature_extra.rs @@ -45,7 +45,7 @@ pub struct CreatureExtraData { } impl CreatureExtraData { - fn add_mod_to_perception_and_skill_mods(self, modifier: i64) -> CreatureExtraData { + fn add_mod_to_perception_and_skill_mods(self, modifier: i64) -> Self { let mut ex_data = self; // we should never have a pwl much greater than perception (pwl=lvl) ex_data.perception = (i64::from(ex_data.perception) + modifier) as i8; @@ -62,12 +62,12 @@ impl CreatureExtraData { ex_data } /// Lowers skill and perception by the given `pwl_mod` - pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureExtraData { + pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> Self { self.add_mod_to_perception_and_skill_mods(-i64::try_from(pwl_mod).unwrap_or(i64::MAX)) } /// Increase/Decrease Perception, and skill modifiers by 2. - pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> CreatureExtraData { + pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> Self { self.add_mod_to_perception_and_skill_mods(variant.to_adjustment_modifier()) } } diff --git a/src/models/creature/creature_component/creature_spell_caster.rs b/src/models/creature/creature_component/creature_spell_caster.rs index ce43836..1f2b46e 100644 --- a/src/models/creature/creature_component/creature_spell_caster.rs +++ b/src/models/creature/creature_component/creature_spell_caster.rs @@ -11,7 +11,7 @@ pub struct CreatureSpellCasterData { } impl CreatureSpellCasterData { - pub fn add_mod_to_spellcaster_atk_and_dc(self, modifier: i64) -> CreatureSpellCasterData { + pub fn add_mod_to_spellcaster_atk_and_dc(self, modifier: i64) -> Self { let mut spell_data = self; spell_data.spell_caster_entry.spell_casting_atk_mod = spell_data @@ -28,14 +28,14 @@ impl CreatureSpellCasterData { } /// Lowers spell caster atk and dc - pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> CreatureSpellCasterData { + pub fn convert_from_base_to_pwl(self, pwl_mod: u64) -> Self { self.add_mod_to_spellcaster_atk_and_dc(-i64::try_from(pwl_mod).unwrap_or(i64::MAX)) } /// Increase/Decrease the damage of its Strikes and other offensive abilities by 2. /// If the creature has limits on how many times or how often it can use an ability /// (such as a spellcaster’s spells or a dragon’s breath), decrease the damage by 4 instead. - pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> CreatureSpellCasterData { + pub fn convert_from_base_to_variant(self, variant: CreatureVariant) -> Self { self.add_mod_to_spellcaster_atk_and_dc(variant.to_adjustment_modifier()) } } diff --git a/src/models/creature/creature_filter_enum.rs b/src/models/creature/creature_filter_enum.rs index 36ca0a9..6a2b21e 100644 --- a/src/models/creature/creature_filter_enum.rs +++ b/src/models/creature/creature_filter_enum.rs @@ -33,43 +33,43 @@ pub enum CreatureFilter { impl fmt::Display for CreatureFilter { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - CreatureFilter::Level => { + Self::Level => { write!(f, "level") } - CreatureFilter::Family => { + Self::Family => { write!(f, "family") } - CreatureFilter::Size => { + Self::Size => { write!(f, "size") } - CreatureFilter::Rarity => { + Self::Rarity => { write!(f, "rarity") } - CreatureFilter::Melee => { + Self::Melee => { write!(f, "is_melee") } - CreatureFilter::Ranged => { + Self::Ranged => { write!(f, "is_ranged") } - CreatureFilter::SpellCaster => { + Self::SpellCaster => { write!(f, "is_spell_caster") } - CreatureFilter::Traits => { + Self::Traits => { write!(f, "traits") } - CreatureFilter::CreatureTypes => { + Self::CreatureTypes => { write!(f, "cr_type") } - CreatureFilter::CreatureRoles => { + Self::CreatureRoles => { write!(f, "creature_roles") } - CreatureFilter::Alignment => { + Self::Alignment => { write!(f, "alignment") } - CreatureFilter::PathfinderVersion => { + Self::PathfinderVersion => { write!(f, "remaster") } - CreatureFilter::Sources => { + Self::Sources => { write!(f, "sources") } } diff --git a/src/models/creature/creature_metadata/alignment_enum.rs b/src/models/creature/creature_metadata/alignment_enum.rs index ac44b66..2460a97 100644 --- a/src/models/creature/creature_metadata/alignment_enum.rs +++ b/src/models/creature/creature_metadata/alignment_enum.rs @@ -56,10 +56,10 @@ pub enum AlignmentEnum { pub const ALIGNMENT_TRAITS: [&str; 4] = ["GOOD", "EVIL", "CHAOTIC", "LAWFUL"]; impl From<(&Vec, bool)> for AlignmentEnum { - fn from(tuple: (&Vec, bool)) -> AlignmentEnum { + fn from(tuple: (&Vec, bool)) -> Self { // If remaster then it's always no alignment if tuple.1 { - return AlignmentEnum::No; + return Self::No; } let string_traits: Vec = tuple.0.iter().map(|x| x.to_uppercase()).collect(); let is_good = string_traits.contains(&"GOOD".to_string()); @@ -68,41 +68,41 @@ impl From<(&Vec, bool)> for AlignmentEnum { let is_lawful = string_traits.contains(&"LAWFUL".to_string()); if is_good { if is_chaos { - return AlignmentEnum::Cg; + return Self::Cg; } if is_lawful { - return AlignmentEnum::Lg; + return Self::Lg; } - return AlignmentEnum::Ng; + return Self::Ng; } if is_evil { if is_chaos { - return AlignmentEnum::Ce; + return Self::Ce; } if is_lawful { - return AlignmentEnum::Le; + return Self::Le; } - return AlignmentEnum::Ne; + return Self::Ne; } if is_chaos { - return AlignmentEnum::Cn; + return Self::Cn; } if is_lawful { - return AlignmentEnum::Ln; + return Self::Ln; } - AlignmentEnum::N + Self::N } } impl From for AlignmentEnum { fn from(value: String) -> Self { - AlignmentEnum::from_str(value.as_str()).unwrap_or_default() + Self::from_str(value.as_str()).unwrap_or_default() } } impl From<&String> for AlignmentEnum { fn from(value: &String) -> Self { - AlignmentEnum::from_str(value.as_str()).unwrap_or_default() + Self::from_str(value.as_str()).unwrap_or_default() } } @@ -110,35 +110,35 @@ impl FromStr for AlignmentEnum { type Err = (); fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { - "CE" => Ok(AlignmentEnum::Ce), - "CN" => Ok(AlignmentEnum::Cn), - "CG" => Ok(AlignmentEnum::Cg), - "NE" => Ok(AlignmentEnum::Ne), - "N" => Ok(AlignmentEnum::N), - "NG" => Ok(AlignmentEnum::Ng), - "LE" => Ok(AlignmentEnum::Le), - "LN" => Ok(AlignmentEnum::Ln), - "LG" => Ok(AlignmentEnum::Lg), - "ANY" => Ok(AlignmentEnum::Any), - _ => Ok(AlignmentEnum::No), + "CE" => Ok(Self::Ce), + "CN" => Ok(Self::Cn), + "CG" => Ok(Self::Cg), + "NE" => Ok(Self::Ne), + "N" => Ok(Self::N), + "NG" => Ok(Self::Ng), + "LE" => Ok(Self::Le), + "LN" => Ok(Self::Ln), + "LG" => Ok(Self::Lg), + "ANY" => Ok(Self::Any), + _ => Ok(Self::No), } } } impl Clone for AlignmentEnum { - fn clone(&self) -> AlignmentEnum { + fn clone(&self) -> Self { match self { - AlignmentEnum::Ce => AlignmentEnum::Ce, - AlignmentEnum::Cn => AlignmentEnum::Cn, - AlignmentEnum::Cg => AlignmentEnum::Cg, - AlignmentEnum::Ne => AlignmentEnum::Ne, - AlignmentEnum::N => AlignmentEnum::N, - AlignmentEnum::Ng => AlignmentEnum::Ng, - AlignmentEnum::Le => AlignmentEnum::Le, - AlignmentEnum::Ln => AlignmentEnum::Ln, - AlignmentEnum::Lg => AlignmentEnum::Lg, - AlignmentEnum::No => AlignmentEnum::No, - AlignmentEnum::Any => AlignmentEnum::Any, + Self::Ce => Self::Ce, + Self::Cn => Self::Cn, + Self::Cg => Self::Cg, + Self::Ne => Self::Ne, + Self::N => Self::N, + Self::Ng => Self::Ng, + Self::Le => Self::Le, + Self::Ln => Self::Ln, + Self::Lg => Self::Lg, + Self::No => Self::No, + Self::Any => Self::Any, } } } diff --git a/src/models/creature/creature_metadata/creature_role.rs b/src/models/creature/creature_metadata/creature_role.rs index 4eef0e4..9e1ffa9 100644 --- a/src/models/creature/creature_metadata/creature_role.rs +++ b/src/models/creature/creature_metadata/creature_role.rs @@ -40,13 +40,13 @@ fn get_dmg_from_regex(raw_str: &str) -> Option { impl CreatureRoleEnum { pub fn to_db_column(&self) -> String { match self { - CreatureRoleEnum::Brute => String::from("brute_percentage"), - CreatureRoleEnum::MagicalStriker => String::from("magical_striker_percentage"), - CreatureRoleEnum::SkillParagon => String::from("skill_paragon_percentage"), - CreatureRoleEnum::Skirmisher => String::from("skirmisher_percentage"), - CreatureRoleEnum::Sniper => String::from("sniper_percentage"), - CreatureRoleEnum::Soldier => String::from("soldier_percentage"), - CreatureRoleEnum::SpellCaster => String::from("spell_caster_percentage"), + Self::Brute => String::from("brute_percentage"), + Self::MagicalStriker => String::from("magical_striker_percentage"), + Self::SkillParagon => String::from("skill_paragon_percentage"), + Self::Skirmisher => String::from("skirmisher_percentage"), + Self::Sniper => String::from("sniper_percentage"), + Self::Soldier => String::from("soldier_percentage"), + Self::SpellCaster => String::from("spell_caster_percentage"), } } pub fn from_creature_with_given_scales( @@ -55,7 +55,7 @@ impl CreatureRoleEnum { cr_combat: &CreatureCombatData, cr_spells: &CreatureSpellCasterData, scales: &CreatureScales, - ) -> BTreeMap { + ) -> BTreeMap { let mut roles = BTreeMap::new(); roles.insert( Self::Brute, @@ -95,7 +95,7 @@ impl CreatureRoleEnum { } pub fn list() -> Vec { - CreatureRoleEnum::iter().map(|x| x.to_string()).collect() + Self::iter().map(|x| x.to_string()).collect() } } // Brute @@ -442,13 +442,13 @@ impl FromStr for CreatureRoleEnum { type Err = (); fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { - "BRUTE" => Ok(CreatureRoleEnum::Brute), - "MAGICAL STRIKER" | "MAGICALSTRIKER" => Ok(CreatureRoleEnum::MagicalStriker), - "SKILL PARAGON" | "SKILLPARAGON" => Ok(CreatureRoleEnum::SkillParagon), - "SKIRMISHER" => Ok(CreatureRoleEnum::Skirmisher), - "SNIPER" => Ok(CreatureRoleEnum::Sniper), - "SOLDIER" => Ok(CreatureRoleEnum::Soldier), - "SPELLCASTER" | "SPELL CASTER" => Ok(CreatureRoleEnum::SpellCaster), + "BRUTE" => Ok(Self::Brute), + "MAGICAL STRIKER" | "MAGICALSTRIKER" => Ok(Self::MagicalStriker), + "SKILL PARAGON" | "SKILLPARAGON" => Ok(Self::SkillParagon), + "SKIRMISHER" => Ok(Self::Skirmisher), + "SNIPER" => Ok(Self::Sniper), + "SOLDIER" => Ok(Self::Soldier), + "SPELLCASTER" | "SPELL CASTER" => Ok(Self::SpellCaster), _ => Err(()), } } @@ -457,25 +457,25 @@ impl FromStr for CreatureRoleEnum { impl fmt::Display for CreatureRoleEnum { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { - CreatureRoleEnum::Brute => { + Self::Brute => { write!(f, "Brute") } - CreatureRoleEnum::MagicalStriker => { + Self::MagicalStriker => { write!(f, "Magical Striker") } - CreatureRoleEnum::SkillParagon => { + Self::SkillParagon => { write!(f, "Skill Paragon") } - CreatureRoleEnum::Skirmisher => { + Self::Skirmisher => { write!(f, "Skirmisher") } - CreatureRoleEnum::Sniper => { + Self::Sniper => { write!(f, "Sniper") } - CreatureRoleEnum::Soldier => { + Self::Soldier => { write!(f, "Soldier") } - CreatureRoleEnum::SpellCaster => { + Self::SpellCaster => { write!(f, "SpellCaster") } } @@ -484,7 +484,7 @@ impl fmt::Display for CreatureRoleEnum { /// Calculate value distance from upper bound, lower than ub value will /// yield 0 -fn calculate_ub_distance(ub: i64, value: i64) -> u16 { +const fn calculate_ub_distance(ub: i64, value: i64) -> u16 { if value > ub { (value - ub).unsigned_abs() as u16 } else { @@ -492,9 +492,9 @@ fn calculate_ub_distance(ub: i64, value: i64) -> u16 { } } -/// Calculate value distance from lower bound, higher than lb value will +/// Calculate value distance from lower bound, `value` higher than `lb` will /// yield 0 -fn calculate_lb_distance(lb: i64, value: i64) -> u16 { +const fn calculate_lb_distance(lb: i64, value: i64) -> u16 { if value < lb { (lb - value).unsigned_abs() as u16 } else { @@ -503,7 +503,7 @@ fn calculate_lb_distance(lb: i64, value: i64) -> u16 { } /// Calculates value distance from bounds, it will exclude upper bound -fn calculate_dist(lb: i64, ub: i64, value: i64) -> u16 { +const fn calculate_dist(lb: i64, ub: i64, value: i64) -> u16 { if value < lb { (lb - value).unsigned_abs() as u16 } else if value >= ub { diff --git a/src/models/creature/creature_metadata/type_enum.rs b/src/models/creature/creature_metadata/type_enum.rs index 3236a27..a304443 100644 --- a/src/models/creature/creature_metadata/type_enum.rs +++ b/src/models/creature/creature_metadata/type_enum.rs @@ -30,19 +30,19 @@ pub enum CreatureTypeEnum { impl From for CreatureTypeEnum { fn from(value: String) -> Self { - CreatureTypeEnum::from_str(value.as_str()).unwrap_or_default() + Self::from_str(value.as_str()).unwrap_or_default() } } impl From> for CreatureTypeEnum { fn from(value: Option) -> Self { - CreatureTypeEnum::from_str(value.unwrap_or_default().as_str()).unwrap_or_default() + Self::from_str(value.unwrap_or_default().as_str()).unwrap_or_default() } } impl From<&Option> for CreatureTypeEnum { fn from(value: &Option) -> Self { - CreatureTypeEnum::from_str(value.clone().unwrap_or_default().as_str()).unwrap_or_default() + Self::from_str(value.clone().unwrap_or_default().as_str()).unwrap_or_default() } } @@ -50,17 +50,17 @@ impl FromStr for CreatureTypeEnum { type Err = (); fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { - "NPC" => Ok(CreatureTypeEnum::Npc), - _ => Ok(CreatureTypeEnum::Monster), + "NPC" => Ok(Self::Npc), + _ => Ok(Self::Monster), } } } impl Clone for CreatureTypeEnum { - fn clone(&self) -> CreatureTypeEnum { + fn clone(&self) -> Self { match self { - CreatureTypeEnum::Monster => CreatureTypeEnum::Monster, - CreatureTypeEnum::Npc => CreatureTypeEnum::Npc, + Self::Monster => Self::Monster, + Self::Npc => Self::Npc, } } } diff --git a/src/models/creature/creature_metadata/variant_enum.rs b/src/models/creature/creature_metadata/variant_enum.rs index 8c68703..c32d186 100644 --- a/src/models/creature/creature_metadata/variant_enum.rs +++ b/src/models/creature/creature_metadata/variant_enum.rs @@ -26,19 +26,19 @@ pub enum CreatureVariant { } impl CreatureVariant { - pub fn to_adjustment_modifier(self) -> i64 { + pub const fn to_adjustment_modifier(self) -> i64 { match self { - CreatureVariant::Weak => -2, - CreatureVariant::Elite => 2, - CreatureVariant::Base => 0, + Self::Weak => -2, + Self::Elite => 2, + Self::Base => 0, } } - pub fn get_variant_level(self, base_lvl: i64) -> i64 { + pub const fn get_variant_level(self, base_lvl: i64) -> i64 { match self { //Decrease the creature’s level by 1; if the creature is level 1, // instead decrease its level by 2. - CreatureVariant::Weak => { + Self::Weak => { if base_lvl == 1 { base_lvl - 2 } else { @@ -47,22 +47,22 @@ impl CreatureVariant { } //Increase the creature’s level by 1; if the creature is level –1 or 0, // instead increase its level by 2. - CreatureVariant::Elite => { + Self::Elite => { if base_lvl == -1 || base_lvl == 0 { base_lvl + 2 } else { base_lvl + 1 } } - CreatureVariant::Base => base_lvl, + Self::Base => base_lvl, } } pub fn get_variant_hp(self, base_hp: i64, starting_lvl: i64) -> i64 { let hp_mod_map = match self { - CreatureVariant::Weak => hp_decrease_by_level(), - CreatureVariant::Elite => hp_increase_by_level(), - CreatureVariant::Base => { + Self::Weak => hp_decrease_by_level(), + Self::Elite => hp_increase_by_level(), + Self::Base => { hashmap! {} } }; @@ -73,14 +73,14 @@ impl CreatureVariant { .keys() .filter(|lvl| starting_lvl >= **lvl) .max() - .unwrap_or(hp_mod_map.keys().next().unwrap_or(&0)); + .unwrap_or_else(|| hp_mod_map.keys().next().unwrap_or(&0)); let hp_mod = *hp_mod_map.get(desired_key).unwrap_or(&0); (base_hp + hp_mod).max(1) } pub fn get_variant_archive_link(self, archive_link: Option) -> Option { match self { - CreatureVariant::Base => archive_link, + Self::Base => archive_link, _ => add_boolean_query(Option::from(&archive_link), &self.to_string(), true), } } diff --git a/src/models/creature/creature_struct.rs b/src/models/creature/creature_struct.rs index 2d63267..7d5c318 100644 --- a/src/models/creature/creature_struct.rs +++ b/src/models/creature/creature_struct.rs @@ -23,7 +23,7 @@ impl Creature { /// Decrease the creature’s HP based on its starting level. /// Decrease the creature’s AC, attack modifiers, DCs, saving throws, Perception, and skill modifiers by 2. /// Decrease the damage of its Strikes and other offensive abilities by 2. If the creature has limits on how many times or how often it can use an ability (such as a spellcaster’s spells or a dragon’s breath), decrease the damage by 4 instead. - pub fn convert_creature_to_variant(self, variant: CreatureVariant) -> Creature { + pub fn convert_creature_to_variant(self, variant: CreatureVariant) -> Self { let mut cr = Self::from_core_with_variant(self.core_data, variant); cr.extra_data = self .extra_data @@ -37,14 +37,14 @@ impl Creature { cr } - pub fn convert_creature_to_pwl(self) -> Creature { + pub fn convert_creature_to_pwl(self) -> Self { let pwl_mod = if self.core_data.essential.base_level >= 0 { self.core_data.essential.base_level.unsigned_abs() } else { 0 }; - Creature { + Self { core_data: self.core_data, variant_data: self.variant_data, extra_data: self.extra_data.map(|x| x.convert_from_base_to_pwl(pwl_mod)), @@ -56,7 +56,7 @@ impl Creature { .map(|x| x.convert_from_base_to_pwl(pwl_mod)), } } - pub fn from_core(core: CreatureCoreData) -> Creature { + pub fn from_core(core: CreatureCoreData) -> Self { let level = core.essential.base_level; let archive_link = core.derived.archive_link.clone(); Self { @@ -75,7 +75,7 @@ impl Creature { pub fn from_core_with_variant( mut core: CreatureCoreData, creature_variant: CreatureVariant, - ) -> Creature { + ) -> Self { let variant_hp = creature_variant.get_variant_hp(core.essential.hp, core.essential.base_level); let variant_archive_link = diff --git a/src/models/encounter_structs.rs b/src/models/encounter_structs.rs index 646f9c6..337696c 100644 --- a/src/models/encounter_structs.rs +++ b/src/models/encounter_structs.rs @@ -78,7 +78,7 @@ pub enum EncounterChallengeEnum { impl From for String { fn from(value: EncounterChallengeEnum) -> Self { - String::from(match value { + Self::from(match value { EncounterChallengeEnum::Trivial => "TRIVIAL", EncounterChallengeEnum::Low => "LOW", EncounterChallengeEnum::Moderate => "MODERATE", @@ -90,14 +90,14 @@ impl From for String { } impl EncounterChallengeEnum { - pub fn rand() -> EncounterChallengeEnum { - match fastrand::usize(0..EncounterChallengeEnum::COUNT) { - 0 => EncounterChallengeEnum::Trivial, - 1 => EncounterChallengeEnum::Low, - 2 => EncounterChallengeEnum::Moderate, - 3 => EncounterChallengeEnum::Severe, - 4 => EncounterChallengeEnum::Extreme, - _ => EncounterChallengeEnum::Impossible, + pub fn rand() -> Self { + match fastrand::usize(0..Self::COUNT) { + 0 => Self::Trivial, + 1 => Self::Low, + 2 => Self::Moderate, + 3 => Self::Severe, + 4 => Self::Extreme, + _ => Self::Impossible, } } } diff --git a/src/models/item/armor_struct.rs b/src/models/item/armor_struct.rs index a0dbadf..ad3d92a 100644 --- a/src/models/item/armor_struct.rs +++ b/src/models/item/armor_struct.rs @@ -33,7 +33,7 @@ pub struct ArmorData { impl<'r> FromRow<'r, SqliteRow> for Armor { fn from_row(row: &'r SqliteRow) -> Result { let item_core = Item::from_row(row)?; - Ok(Armor { + Ok(Self { item_core, armor_data: ArmorData { id: row.try_get("armor_id")?, diff --git a/src/models/item/item_metadata/type_enum.rs b/src/models/item/item_metadata/type_enum.rs index 3b19242..57f8e42 100644 --- a/src/models/item/item_metadata/type_enum.rs +++ b/src/models/item/item_metadata/type_enum.rs @@ -34,22 +34,22 @@ impl ItemTypeEnum { /// It returns the generic table name of the given item type. fn to_db_table_name(&self) -> String { String::from(match self { - ItemTypeEnum::Consumable | ItemTypeEnum::Equipment => "ITEM", - ItemTypeEnum::Weapon => "WEAPON", - ItemTypeEnum::Armor => "ARMOR", - ItemTypeEnum::Shield => "SHIELD", + Self::Consumable | Self::Equipment => "ITEM", + Self::Weapon => "WEAPON", + Self::Armor => "ARMOR", + Self::Shield => "SHIELD", }) } } impl Clone for ItemTypeEnum { - fn clone(&self) -> ItemTypeEnum { + fn clone(&self) -> Self { match self { - ItemTypeEnum::Consumable => ItemTypeEnum::Consumable, - ItemTypeEnum::Equipment => ItemTypeEnum::Equipment, - ItemTypeEnum::Armor => ItemTypeEnum::Armor, - ItemTypeEnum::Weapon => ItemTypeEnum::Weapon, - ItemTypeEnum::Shield => ItemTypeEnum::Shield, + Self::Consumable => Self::Consumable, + Self::Equipment => Self::Equipment, + Self::Armor => Self::Armor, + Self::Weapon => Self::Weapon, + Self::Shield => Self::Shield, } } } @@ -58,11 +58,11 @@ impl FromStr for ItemTypeEnum { type Err = (); fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { - "CONSUMABLE" => Ok(ItemTypeEnum::Consumable), - "EQUIPMENT" => Ok(ItemTypeEnum::Equipment), - "WEAPON" => Ok(ItemTypeEnum::Weapon), - "ARMOR" => Ok(ItemTypeEnum::Armor), - "SHIELD" => Ok(ItemTypeEnum::Shield), + "CONSUMABLE" => Ok(Self::Consumable), + "EQUIPMENT" => Ok(Self::Equipment), + "WEAPON" => Ok(Self::Weapon), + "ARMOR" => Ok(Self::Armor), + "SHIELD" => Ok(Self::Shield), _ => Err(()), } } @@ -71,19 +71,19 @@ impl FromStr for ItemTypeEnum { impl Display for ItemTypeEnum { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - ItemTypeEnum::Consumable => { + Self::Consumable => { write!(f, "consumable") } - ItemTypeEnum::Equipment => { + Self::Equipment => { write!(f, "equipment") } - ItemTypeEnum::Weapon => { + Self::Weapon => { write!(f, "weapon") } - ItemTypeEnum::Armor => { + Self::Armor => { write!(f, "armor") } - ItemTypeEnum::Shield => { + Self::Shield => { write!(f, "shield") } } @@ -103,11 +103,11 @@ pub enum WeaponTypeEnum { } impl Clone for WeaponTypeEnum { - fn clone(&self) -> WeaponTypeEnum { + fn clone(&self) -> Self { match self { - WeaponTypeEnum::Ranged => WeaponTypeEnum::Ranged, - WeaponTypeEnum::Melee => WeaponTypeEnum::Melee, - WeaponTypeEnum::Generic => WeaponTypeEnum::Generic, + Self::Ranged => Self::Ranged, + Self::Melee => Self::Melee, + Self::Generic => Self::Generic, } } } @@ -116,9 +116,9 @@ impl FromStr for WeaponTypeEnum { type Err = (); fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { - "RANGED" => Ok(WeaponTypeEnum::Ranged), - "MELEE" => Ok(WeaponTypeEnum::Melee), - "GENERIC" => Ok(WeaponTypeEnum::Generic), + "RANGED" => Ok(Self::Ranged), + "MELEE" => Ok(Self::Melee), + "GENERIC" => Ok(Self::Generic), _ => Err(()), } } @@ -127,13 +127,13 @@ impl FromStr for WeaponTypeEnum { impl Display for WeaponTypeEnum { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - WeaponTypeEnum::Melee => { + Self::Melee => { write!(f, "melee") } - WeaponTypeEnum::Ranged => { + Self::Ranged => { write!(f, "ranged") } - WeaponTypeEnum::Generic => { + Self::Generic => { write!(f, "generic") } } diff --git a/src/models/item/item_struct.rs b/src/models/item/item_struct.rs index a35559b..07f8328 100644 --- a/src/models/item/item_struct.rs +++ b/src/models/item/item_struct.rs @@ -55,7 +55,7 @@ impl<'r> FromRow<'r, SqliteRow> for Item { let size: String = row.try_get("size")?; let type_str: String = row.try_get("item_type")?; let bulk: f64 = row.try_get("bulk")?; - Ok(Item { + Ok(Self { id: row.try_get("id")?, name: row.try_get("name")?, bulk: OrderedFloat::from(bulk), diff --git a/src/models/item/shield_struct.rs b/src/models/item/shield_struct.rs index f42a497..8820a34 100644 --- a/src/models/item/shield_struct.rs +++ b/src/models/item/shield_struct.rs @@ -24,7 +24,7 @@ pub struct ShieldData { impl<'r> FromRow<'r, SqliteRow> for Shield { fn from_row(row: &'r SqliteRow) -> Result { let item_core = Item::from_row(row)?; - Ok(Shield { + Ok(Self { item_core, shield_data: ShieldData { id: row.try_get("shield_id")?, diff --git a/src/models/item/weapon_struct.rs b/src/models/item/weapon_struct.rs index 9969741..d25de54 100644 --- a/src/models/item/weapon_struct.rs +++ b/src/models/item/weapon_struct.rs @@ -36,7 +36,7 @@ impl<'r> FromRow<'r, SqliteRow> for Weapon { fn from_row(row: &'r SqliteRow) -> Result { let item_core = Item::from_row(row)?; let wp_type = WeaponTypeEnum::from_str(row.try_get("weapon_type")?); - Ok(Weapon { + Ok(Self { item_core, weapon_data: WeaponData { id: row.try_get("weapon_id")?, @@ -61,11 +61,7 @@ impl Weapon { .iter() .map(|x| { let b = x.bonus_dmg as f64; - if let Some(dice) = x.clone().dice { - dice.get_avg_dmg(b) - } else { - 0 - } + x.clone().dice.map_or(0, |dice| dice.get_avg_dmg(b)) }) .sum() } @@ -82,7 +78,7 @@ pub struct DamageData { impl<'r> FromRow<'r, SqliteRow> for DamageData { fn from_row(row: &'r SqliteRow) -> Result { - Ok(DamageData { + Ok(Self { id: row.try_get("id")?, bonus_dmg: row.try_get("bonus_dmg")?, dmg_type: row.try_get("dmg_type").ok(), diff --git a/src/models/pf_version_enum.rs b/src/models/pf_version_enum.rs index 8e607fd..0ec2ba2 100644 --- a/src/models/pf_version_enum.rs +++ b/src/models/pf_version_enum.rs @@ -20,9 +20,9 @@ impl PathfinderVersionEnum { match self { // The db column is a boolean called "remaster" so we translate the enum to // FALSE if legacy, TRUE if remaster and TRUE, FALSE if both - PathfinderVersionEnum::Legacy => vec![String::from("FALSE")], - PathfinderVersionEnum::Remaster => vec![String::from("TRUE")], - PathfinderVersionEnum::Any => { + Self::Legacy => vec![String::from("FALSE")], + Self::Remaster => vec![String::from("TRUE")], + Self::Any => { vec![String::from("TRUE"), String::from("FALSE")] } } diff --git a/src/models/response_data.rs b/src/models/response_data.rs index 45d892d..908a87c 100644 --- a/src/models/response_data.rs +++ b/src/models/response_data.rs @@ -19,7 +19,7 @@ pub struct ResponseDataModifiers { pub spell_casting_data: Option, } -#[derive(Serialize, Deserialize, Clone, ToSchema, Hash, PartialEq)] +#[derive(Serialize, Deserialize, Clone, ToSchema, Hash, PartialEq, Eq)] pub struct ResponseCreature { pub core_data: CreatureCoreData, pub variant_data: CreatureVariantData, diff --git a/src/models/routers_validator_structs.rs b/src/models/routers_validator_structs.rs index ca55bf9..837e6a1 100644 --- a/src/models/routers_validator_structs.rs +++ b/src/models/routers_validator_structs.rs @@ -92,7 +92,7 @@ pub struct PaginatedRequest { impl Default for PaginatedRequest { fn default() -> Self { - PaginatedRequest { + Self { cursor: 0, page_size: 100, } @@ -137,24 +137,24 @@ impl Dice { let m = f64::from(self.dice_size); let n = f64::from(self.n_of_dices); let b = bonus_dmg; - let avg: f64 = (((m + 1.) / 2.) * n) + b; + let avg: f64 = ((m + 1.) / 2.).mul_add(n, b); avg.floor() as i64 } - pub fn from_optional_dice_number_and_size( + pub const fn from_optional_dice_number_and_size( n_of_dices: Option, dice_size: Option, - ) -> Option { + ) -> Option { match (n_of_dices, dice_size) { - (Some(n), Some(s)) => Some(Dice { + (Some(n), Some(s)) => Some(Self { n_of_dices: n, dice_size: s, }), - (None, Some(s)) => Some(Dice { + (None, Some(s)) => Some(Self { n_of_dices: 1, dice_size: s, }), - (Some(n), None) => Some(Dice { + (Some(n), None) => Some(Self { n_of_dices: n, dice_size: 1, }), diff --git a/src/models/shared/rarity_enum.rs b/src/models/shared/rarity_enum.rs index 0841261..46dfb46 100644 --- a/src/models/shared/rarity_enum.rs +++ b/src/models/shared/rarity_enum.rs @@ -21,7 +21,7 @@ pub enum RarityEnum { impl From for RarityEnum { fn from(value: String) -> Self { - RarityEnum::from_str(value.as_str()).unwrap_or_default() + Self::from_str(value.as_str()).unwrap_or_default() } } @@ -29,22 +29,22 @@ impl FromStr for RarityEnum { type Err = (); fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { - "COMMON" => Ok(RarityEnum::Common), - "UNCOMMON" => Ok(RarityEnum::Uncommon), - "RARE" => Ok(RarityEnum::Rare), - "UNIQUE" => Ok(RarityEnum::Unique), + "COMMON" => Ok(Self::Common), + "UNCOMMON" => Ok(Self::Uncommon), + "RARE" => Ok(Self::Rare), + "UNIQUE" => Ok(Self::Unique), _ => Err(()), } } } impl Clone for RarityEnum { - fn clone(&self) -> RarityEnum { + fn clone(&self) -> Self { match self { - RarityEnum::Common => RarityEnum::Common, - RarityEnum::Uncommon => RarityEnum::Uncommon, - RarityEnum::Rare => RarityEnum::Rare, - RarityEnum::Unique => RarityEnum::Unique, + Self::Common => Self::Common, + Self::Uncommon => Self::Uncommon, + Self::Rare => Self::Rare, + Self::Unique => Self::Unique, } } } diff --git a/src/models/shared/size_enum.rs b/src/models/shared/size_enum.rs index f8a255a..18ddbdf 100644 --- a/src/models/shared/size_enum.rs +++ b/src/models/shared/size_enum.rs @@ -24,21 +24,21 @@ pub enum SizeEnum { } impl Clone for SizeEnum { - fn clone(&self) -> SizeEnum { + fn clone(&self) -> Self { match self { - SizeEnum::Tiny => SizeEnum::Tiny, - SizeEnum::Small => SizeEnum::Small, - SizeEnum::Medium => SizeEnum::Medium, - SizeEnum::Large => SizeEnum::Large, - SizeEnum::Huge => SizeEnum::Huge, - SizeEnum::Gargantuan => SizeEnum::Gargantuan, + Self::Tiny => Self::Tiny, + Self::Small => Self::Small, + Self::Medium => Self::Medium, + Self::Large => Self::Large, + Self::Huge => Self::Huge, + Self::Gargantuan => Self::Gargantuan, } } } impl From for SizeEnum { fn from(value: String) -> Self { - SizeEnum::from_str(value.as_str()).unwrap_or_default() + Self::from_str(value.as_str()).unwrap_or_default() } } @@ -46,12 +46,12 @@ impl FromStr for SizeEnum { type Err = (); fn from_str(s: &str) -> Result { match s.to_uppercase().as_str() { - "TINY" => Ok(SizeEnum::Tiny), - "SMALL" => Ok(SizeEnum::Small), - "MEDIUM" => Ok(SizeEnum::Medium), - "LARGE" => Ok(SizeEnum::Large), - "HUGE" => Ok(SizeEnum::Huge), - "GARGANTUAN" => Ok(SizeEnum::Gargantuan), + "TINY" => Ok(Self::Tiny), + "SMALL" => Ok(Self::Small), + "MEDIUM" => Ok(Self::Medium), + "LARGE" => Ok(Self::Large), + "HUGE" => Ok(Self::Huge), + "GARGANTUAN" => Ok(Self::Gargantuan), _ => Err(()), } } diff --git a/src/models/shop_structs.rs b/src/models/shop_structs.rs index cae51b1..833f33e 100644 --- a/src/models/shop_structs.rs +++ b/src/models/shop_structs.rs @@ -30,7 +30,7 @@ pub struct ShopTemplateData { impl From for ShopTemplateData { fn from(template_enum: ShopTemplateEnum) -> Self { let (e_p, w_p, a_p, s_p) = template_enum.get_equippable_percentages(); - ShopTemplateData { + Self { name: template_enum.to_string(), description: template_enum.get_description(), equipment_percentage: e_p, @@ -68,17 +68,17 @@ pub enum ShopTemplateEnum { impl ShopTemplateEnum { /// Returns percentage of equipment, weapons, armor, shield for the given template - pub fn get_equippable_percentages(&self) -> (u8, u8, u8, u8) { + pub const fn get_equippable_percentages(&self) -> (u8, u8, u8, u8) { match self { - ShopTemplateEnum::Blacksmith => (10, 40, 25, 25), - ShopTemplateEnum::Alchemist => (100, 0, 0, 0), - ShopTemplateEnum::General => (70, 10, 10, 10), + Self::Blacksmith => (10, 40, 25, 25), + Self::Alchemist => (100, 0, 0, 0), + Self::General => (70, 10, 10, 10), } } pub fn get_allowed_item_types(&self) -> Vec { match self { - ShopTemplateEnum::Blacksmith | ShopTemplateEnum::General => { + Self::Blacksmith | Self::General => { vec![ ItemTypeEnum::Armor, ItemTypeEnum::Shield, @@ -87,7 +87,7 @@ impl ShopTemplateEnum { ItemTypeEnum::Equipment, ] } - ShopTemplateEnum::Alchemist => { + Self::Alchemist => { vec![ItemTypeEnum::Consumable, ItemTypeEnum::Equipment] } } @@ -95,9 +95,7 @@ impl ShopTemplateEnum { pub fn get_allowed_item_rarities(&self) -> Vec { match self { - ShopTemplateEnum::Blacksmith - | ShopTemplateEnum::Alchemist - | ShopTemplateEnum::General => { + Self::Blacksmith | Self::Alchemist | Self::General => { vec![RarityEnum::Common, RarityEnum::Uncommon, RarityEnum::Rare] } } @@ -106,10 +104,10 @@ impl ShopTemplateEnum { pub fn get_traits_whitelist(&self) -> Vec { // For future-proof, right now contains 0 logic match self { - ShopTemplateEnum::Blacksmith | ShopTemplateEnum::General => { + Self::Blacksmith | Self::General => { vec![] } - ShopTemplateEnum::Alchemist => { + Self::Alchemist => { vec![ "Alchemical".to_string(), "Bomb".to_string(), @@ -120,11 +118,9 @@ impl ShopTemplateEnum { } } - pub fn get_traits_blacklist(&self) -> Vec { + pub const fn get_traits_blacklist(&self) -> Vec { match self { - ShopTemplateEnum::Blacksmith - | ShopTemplateEnum::Alchemist - | ShopTemplateEnum::General => { + Self::Blacksmith | Self::Alchemist | Self::General => { vec![] } } @@ -132,13 +128,11 @@ impl ShopTemplateEnum { pub fn get_description(&self) -> String { String::from(match self { - ShopTemplateEnum::Blacksmith => { + Self::Blacksmith => { "Mainly weapons, armors and shields, sometimes equipment and consumables" } - ShopTemplateEnum::Alchemist => { - "Only equipment and consumables, no weapons, armors or shields" - } - ShopTemplateEnum::General => "All kinds of items", + Self::Alchemist => "Only equipment and consumables, no weapons, armors or shields", + Self::General => "All kinds of items", }) } } diff --git a/src/services/encounter_handler/difficulty_utilities.rs b/src/services/encounter_handler/difficulty_utilities.rs index fc69be6..0a768fb 100644 --- a/src/services/encounter_handler/difficulty_utilities.rs +++ b/src/services/encounter_handler/difficulty_utilities.rs @@ -1,6 +1,9 @@ use crate::models::encounter_structs::{EncounterChallengeEnum, ExpRange}; -pub fn scale_difficulty_exp(base_difficulty: &EncounterChallengeEnum, party_size: i64) -> ExpRange { +pub const fn scale_difficulty_exp( + base_difficulty: &EncounterChallengeEnum, + party_size: i64, +) -> ExpRange { // Given the base difficulty and the party size, it scales the base difficulty. // Useful when a party is not the canon 4 party member. let party_deviation = party_size - 4; @@ -17,7 +20,7 @@ pub fn scale_difficulty_exp(base_difficulty: &EncounterChallengeEnum, party_size } } -fn convert_difficulty_enum_to_base_xp_budget(diff: &EncounterChallengeEnum) -> i64 { +const fn convert_difficulty_enum_to_base_xp_budget(diff: &EncounterChallengeEnum) -> i64 { match diff { EncounterChallengeEnum::Trivial => 40, EncounterChallengeEnum::Low => 60, @@ -28,7 +31,7 @@ fn convert_difficulty_enum_to_base_xp_budget(diff: &EncounterChallengeEnum) -> i } } -fn convert_difficulty_enum_to_xp_adjustment(diff: &EncounterChallengeEnum) -> i64 { +const fn convert_difficulty_enum_to_xp_adjustment(diff: &EncounterChallengeEnum) -> i64 { match diff { EncounterChallengeEnum::Trivial => 10, EncounterChallengeEnum::Low => 15, @@ -39,7 +42,7 @@ fn convert_difficulty_enum_to_xp_adjustment(diff: &EncounterChallengeEnum) -> i6 } } -fn get_next_difficulty(diff: &EncounterChallengeEnum) -> EncounterChallengeEnum { +const fn get_next_difficulty(diff: &EncounterChallengeEnum) -> EncounterChallengeEnum { match diff { EncounterChallengeEnum::Trivial => EncounterChallengeEnum::Low, EncounterChallengeEnum::Low => EncounterChallengeEnum::Moderate, diff --git a/src/services/encounter_handler/encounter_calculator.rs b/src/services/encounter_handler/encounter_calculator.rs index 8987577..16741e0 100644 --- a/src/services/encounter_handler/encounter_calculator.rs +++ b/src/services/encounter_handler/encounter_calculator.rs @@ -6,10 +6,10 @@ use std::ops::Neg; use strum::IntoEnumIterator; fn calculate_max_lvl_diff(lvl_and_exp_map: &HashMap) -> i64 { - match lvl_and_exp_map.keys().min() { - None => panic!("No valid lvl and exp map was passed. Abort"), - Some(max_lvl_diff) => *max_lvl_diff, - } + lvl_and_exp_map.keys().min().map_or_else( + || panic!("No valid lvl and exp map was passed. Abort"), + |max_lvl_diff| *max_lvl_diff, + ) } fn calculate_lvl_and_exp_map(is_pwl_on: bool) -> HashMap { @@ -167,16 +167,18 @@ fn convert_lvl_diff_into_exp( lvl_and_exp_map: &HashMap, ) -> i64 { let lvl_diff_rounded_down = lvl_diff.floor() as i64; - lvl_and_exp_map.get(&lvl_diff_rounded_down).map_or( - if lvl_diff_rounded_down < calculate_max_lvl_diff(lvl_and_exp_map) { - 0 - } else { - // To avoid the party of 50 level 1 pg destroying a lvl 20 - scale_difficulty_exp( - &EncounterChallengeEnum::Impossible, - i64::try_from(party_size).unwrap_or(i64::MAX), - ) - .lower_bound + lvl_and_exp_map.get(&lvl_diff_rounded_down).map_or_else( + || { + if lvl_diff_rounded_down < calculate_max_lvl_diff(lvl_and_exp_map) { + 0 + } else { + // To avoid the party of 50 level 1 pg destroying a lvl 20 + scale_difficulty_exp( + &EncounterChallengeEnum::Impossible, + i64::try_from(party_size).unwrap_or(i64::MAX), + ) + .lower_bound + } }, |value| value.abs(), ) diff --git a/src/services/encounter_service.rs b/src/services/encounter_service.rs index 1956360..d74cb33 100644 --- a/src/services/encounter_service.rs +++ b/src/services/encounter_service.rs @@ -293,11 +293,10 @@ async fn get_filtered_creatures( } fn get_lvl_combinations(enc_data: &RandomEncounterData, party_levels: &[i64]) -> HashSet> { - if let Some(adv_group) = enc_data.adventure_group.as_ref() { - get_adventure_group_lvl_combinations(adv_group, party_levels) - } else { - get_standard_lvl_combinations(enc_data, party_levels) - } + enc_data.adventure_group.as_ref().map_or_else( + || get_standard_lvl_combinations(enc_data, party_levels), + |adv_group| get_adventure_group_lvl_combinations(adv_group, party_levels), + ) } fn get_standard_lvl_combinations( @@ -307,7 +306,7 @@ fn get_standard_lvl_combinations( let enc_diff = enc_data .challenge .clone() - .unwrap_or(EncounterChallengeEnum::rand()); + .unwrap_or_else(EncounterChallengeEnum::rand); let lvl_combinations = encounter_calculator::calculate_lvl_combination_for_encounter( &enc_diff, party_levels, diff --git a/src/services/shop_service.rs b/src/services/shop_service.rs index 1a929c6..961c1ae 100644 --- a/src/services/shop_service.rs +++ b/src/services/shop_service.rs @@ -45,14 +45,15 @@ pub async fn generate_random_shop_listing( app_state: &AppState, shop_data: &RandomShopData, ) -> ShopListingResponse { - let (type_filter, rarity_filter) = if let Some(x) = shop_data.shop_template.clone() { - (x.get_allowed_item_types(), x.get_allowed_item_rarities()) - } else { - ( - shop_data.type_filter.clone().unwrap_or_default(), - shop_data.rarity_filter.clone().unwrap_or_default(), - ) - }; + let (type_filter, rarity_filter) = shop_data.shop_template.clone().map_or_else( + || { + ( + shop_data.type_filter.clone().unwrap_or_default(), + shop_data.rarity_filter.clone().unwrap_or_default(), + ) + }, + |x| (x.get_allowed_item_types(), x.get_allowed_item_rarities()), + ); let shop_type = shop_data.shop_template.clone().unwrap_or_default(); let n_of_consumables = i64::from( shop_data @@ -91,7 +92,7 @@ pub async fn generate_random_shop_listing( }, ) { - match shop_proxy::get_filtered_items( + (shop_proxy::get_filtered_items( app_state, &ShopFilterQuery { item_table_fields_filter: ItemTableFieldsFilter { @@ -123,19 +124,19 @@ pub async fn generate_random_shop_listing( n_of_shields, }, ) - .await - { - Ok(result) => { - let n_of_items = result.len(); - ShopListingResponse { - results: Some(result.into_iter().map(ResponseItem::from).collect()), - count: n_of_items, - next: None, - total: n_of_items, - } - } - Err(_) => ShopListingResponse::default(), - } + .await) + .map_or_else( + |_| ShopListingResponse::default(), + |result| { + let n_of_items = result.len(); + ShopListingResponse { + results: Some(result.into_iter().map(ResponseItem::from).collect()), + count: n_of_items, + next: None, + total: n_of_items, + } + }, + ) } else { ShopListingResponse::default() } From 703ee5f4569c4292b7f26f3d85a690e0854206c7 Mon Sep 17 00:00:00 2001 From: rakuja Date: Sun, 8 Dec 2024 11:24:52 +0100 Subject: [PATCH 08/18] chore: clean Cargo.toml --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index b2af5de..fcd5995 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,8 +40,8 @@ cast_precision_loss = {level = "allow", priority = 2} future_not_send = {level = "allow", priority = 2} pedantic = {level = "warn", priority = 1} -#restriction = "warn" nursery = {level= "warn", priority = 1} +# unwrap_used = {level = "warn", priority = 1} [dependencies] From fcc8daf30aee0530a7043efe35e3164e7ccdf8ac Mon Sep 17 00:00:00 2001 From: rakuja Date: Sun, 8 Dec 2024 12:48:52 +0100 Subject: [PATCH 09/18] feat: add trait filters to bestiary listing --- src/db/bestiary_proxy.rs | 2 +- src/models/creature/creature_struct.rs | 16 ++++++++++++++++ src/models/routers_validator_structs.rs | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/db/bestiary_proxy.rs b/src/db/bestiary_proxy.rs index dfa07b2..78dd7a0 100644 --- a/src/db/bestiary_proxy.rs +++ b/src/db/bestiary_proxy.rs @@ -227,12 +227,12 @@ async fn get_all_keys(app_state: &AppState) -> FieldsUniqueValuesStruct { /// Gets all the creature core data from the DB. It will not fetch data outside of variant and core. /// It will cache the result. -#[once(sync_writes = true, result = true)] async fn get_all_creatures_from_db(app_state: &AppState) -> Result> { creature_fetcher::fetch_creatures_core_data(&app_state.conn, 0, -1).await } /// Infallible method, it will expose a vector representing the values fetched from db or empty vec +#[once(sync_writes = true)] async fn get_list(app_state: &AppState, variant: CreatureVariant) -> Vec { if let Ok(creatures) = get_all_creatures_from_db(app_state).await { return match variant { diff --git a/src/models/creature/creature_struct.rs b/src/models/creature/creature_struct.rs index 7d5c318..d2c091a 100644 --- a/src/models/creature/creature_struct.rs +++ b/src/models/creature/creature_struct.rs @@ -186,6 +186,22 @@ impl Creature { .to_lowercase() .contains(fam.to_lowercase().as_str()) }) + }) && filters.trait_whitelist_filter.as_ref().map_or(true, |x| { + x.iter().any(|filter_trait| { + self.core_data.traits.iter().any(|cr_trait| { + cr_trait + .to_lowercase() + .contains(filter_trait.to_lowercase().as_str()) + }) + }) + }) && !filters.trait_blacklist_filter.as_ref().map_or(false, |x| { + x.iter().any(|filter_trait| { + self.core_data.traits.iter().any(|cr_trait| { + cr_trait + .to_lowercase() + .eq(filter_trait.to_lowercase().as_str()) + }) + }) }) } } diff --git a/src/models/routers_validator_structs.rs b/src/models/routers_validator_structs.rs index 837e6a1..7832fd8 100644 --- a/src/models/routers_validator_structs.rs +++ b/src/models/routers_validator_structs.rs @@ -16,6 +16,8 @@ pub struct CreatureFieldFilters { pub rarity_filter: Option>, pub size_filter: Option>, pub alignment_filter: Option>, + pub trait_whitelist_filter: Option>, + pub trait_blacklist_filter: Option>, pub role_filter: Option>, pub type_filter: Option>, #[schema(minimum = 0, maximum = 100, example = 50)] From 204ecd08192da7613219facd3d9db9007a7e4d6e Mon Sep 17 00:00:00 2001 From: rakuja Date: Sun, 8 Dec 2024 14:07:11 +0100 Subject: [PATCH 10/18] fix: indexing endpoint correctly --- src/routes/bestiary.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/routes/bestiary.rs b/src/routes/bestiary.rs index 2cf142a..ecc8f2b 100644 --- a/src/routes/bestiary.rs +++ b/src/routes/bestiary.rs @@ -43,6 +43,7 @@ pub fn init_endpoints(cfg: &mut web::ServiceConfig) { cfg.service( web::scope("/bestiary") .service(get_bestiary) + .service(get_bestiary_listing) .service(get_elite_creature) .service(get_weak_creature) .service(get_creature) From 2fdafc165b5e62eae261f8ea3692d076dac62755 Mon Sep 17 00:00:00 2001 From: rakuja Date: Mon, 9 Dec 2024 01:59:12 +0100 Subject: [PATCH 11/18] feat: align random encounter with shop, fix pagination bug (-1) --- src/db/bestiary_proxy.rs | 83 ++++--- src/db/data_providers/creature_fetcher.rs | 7 +- src/db/data_providers/raw_query_builder.rs | 215 ++++++++++-------- src/db/shop_proxy.rs | 6 +- src/models/bestiary_structs.rs | 41 ++++ .../creature_component/filter_struct.rs | 19 -- src/models/creature/creature_component/mod.rs | 1 - .../creature_metadata/alignment_enum.rs | 39 ++++ src/models/encounter_structs.rs | 21 +- src/models/item/item_fields_enum.rs | 37 --- src/models/item/mod.rs | 1 - src/services/encounter_service.rs | 122 ++++------ 12 files changed, 308 insertions(+), 284 deletions(-) delete mode 100644 src/models/creature/creature_component/filter_struct.rs delete mode 100644 src/models/item/item_fields_enum.rs diff --git a/src/db/bestiary_proxy.rs b/src/db/bestiary_proxy.rs index 78dd7a0..52c7af5 100644 --- a/src/db/bestiary_proxy.rs +++ b/src/db/bestiary_proxy.rs @@ -1,9 +1,11 @@ use crate::models::creature::creature_struct::Creature; -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use crate::db::data_providers::creature_fetcher::fetch_traits_associated_with_creatures; use crate::db::data_providers::{creature_fetcher, generic_fetcher}; -use crate::models::bestiary_structs::{BestiaryPaginatedRequest, CreatureSortEnum}; +use crate::models::bestiary_structs::{ + BestiaryFilterQuery, BestiaryPaginatedRequest, CreatureSortEnum, +}; use crate::models::creature::creature_component::creature_core::CreatureCoreData; use crate::models::creature::creature_filter_enum::{CreatureFilter, FieldsUniqueValuesStruct}; use crate::models::creature::creature_metadata::alignment_enum::AlignmentEnum; @@ -16,6 +18,7 @@ use crate::models::routers_validator_structs::{CreatureFieldFilters, OrderEnum}; use crate::AppState; use anyhow::Result; use cached::proc_macro::once; +use itertools::Itertools; use strum::IntoEnumIterator; pub async fn get_creature_by_id( @@ -105,11 +108,14 @@ pub async fn get_paginated_creatures( OrderEnum::Descending => cmp.reverse(), } }); - let curr_slice: Vec = filtered_list .iter() .skip(pagination.paginated_request.cursor as usize) - .take(pagination.paginated_request.page_size.unsigned_abs() as usize) + .take(if pagination.paginated_request.page_size >= 0 { + pagination.paginated_request.page_size.unsigned_abs() as usize + } else { + usize::MAX + }) .cloned() .collect(); @@ -118,16 +124,24 @@ pub async fn get_paginated_creatures( pub async fn get_creatures_passing_all_filters( app_state: &AppState, - key_value_filters: HashMap>, + filters: &BestiaryFilterQuery, fetch_weak: bool, fetch_elite: bool, ) -> Result> { let mut creature_vec = Vec::new(); - let level_vec = key_value_filters - .get(&CreatureFilter::Level) - .map_or_else(HashSet::new, std::clone::Clone::clone); - let modified_filters = - prepare_filters_for_db_communication(key_value_filters, fetch_weak, fetch_elite); + let level_vec = filters.creature_table_fields_filter.level_filter.clone(); + let mut modified_filters = filters.clone(); + modified_filters.creature_table_fields_filter.level_filter = + prepare_level_filter_for_db_communication( + filters + .creature_table_fields_filter + .level_filter + .clone() + .into_iter(), + fetch_weak, + fetch_elite, + ); + for core in creature_fetcher::fetch_creatures_core_data_with_filters(&app_state.conn, &modified_filters) .await? @@ -137,13 +151,13 @@ pub async fn get_creatures_passing_all_filters( // mean that if we have [0,1,2,3] in the filter and allow_elite => [-1,0,1,2,3] then // a creature of level 1 will always be considered the elite variant of level 0. We'll // duplicate the data and will have a base 0 for level 0 and elite 0 for level 1 - if fetch_weak && level_vec.contains(&(core.essential.base_level - 1).to_string()) { + if fetch_weak && level_vec.contains(&(core.essential.base_level - 1)) { creature_vec.push(Creature::from_core_with_variant( core.clone(), CreatureVariant::Weak, )); } - if fetch_elite && level_vec.contains(&(core.essential.base_level + 1).to_string()) { + if fetch_elite && level_vec.contains(&(core.essential.base_level + 1)) { creature_vec.push(Creature::from_core_with_variant( core.clone(), CreatureVariant::Elite, @@ -261,30 +275,27 @@ pub fn order_list_by_level(creature_list: &[Creature]) -> HashMap weak = level -fn prepare_filters_for_db_communication( - key_value_filters: HashMap>, +fn prepare_level_filter_for_db_communication( + level_filter: I, fetch_weak: bool, fetch_elite: bool, -) -> HashMap> { - key_value_filters - .into_iter() - .map(|(key, values)| match key { - CreatureFilter::Level => { - let mut new_values = HashSet::new(); - for str_level in values { - if let Ok(level) = str_level.parse::() { - if fetch_weak { - new_values.insert((level + 1).to_string()); - } - if fetch_elite { - new_values.insert((level - 1).to_string()); - } - new_values.insert(level.to_string()); - } - } - (key, new_values) - } - _ => (key, values), - }) - .collect() +) -> Vec +where + I: Iterator, +{ + // do not remove sorted, it would break contract with merge and dedup + let levels = level_filter.sorted().collect::>(); + let levels_for_elite: Vec = if fetch_elite { + levels.iter().map(|x| x - 1).collect() + } else { + vec![] + }; + let levels_for_weak: Vec = if fetch_weak { + levels.iter().map(|x| x + 1).collect() + } else { + vec![] + }; + + let x = itertools::merge(levels_for_elite, levels_for_weak).collect::>(); + itertools::merge(x, levels).dedup().collect::>() } diff --git a/src/db/data_providers/creature_fetcher.rs b/src/db/data_providers/creature_fetcher.rs index 017faa5..c46ebeb 100644 --- a/src/db/data_providers/creature_fetcher.rs +++ b/src/db/data_providers/creature_fetcher.rs @@ -3,6 +3,7 @@ use crate::db::data_providers::generic_fetcher::{ fetch_weapon_damage_data, fetch_weapon_runes, fetch_weapon_traits, MyString, }; use crate::db::data_providers::raw_query_builder::prepare_filtered_get_creatures_core; +use crate::models::bestiary_structs::BestiaryFilterQuery; use crate::models::creature::creature_component::creature_combat::{ CreatureCombatData, SavingThrows, }; @@ -12,7 +13,6 @@ use crate::models::creature::creature_component::creature_extra::{ }; use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellCasterData; use crate::models::creature::creature_component::creature_variant::CreatureVariantData; -use crate::models::creature::creature_filter_enum::CreatureFilter; use crate::models::creature::creature_metadata::alignment_enum::ALIGNMENT_TRAITS; use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; use crate::models::creature::creature_struct::Creature; @@ -46,7 +46,6 @@ use crate::models::scales_struct::strike_bonus_scales::StrikeBonusScales; use crate::models::scales_struct::strike_dmg_scales::StrikeDmgScales; use anyhow::Result; use sqlx::{Pool, Sqlite}; -use std::collections::{HashMap, HashSet}; async fn fetch_creature_immunities( conn: &Pool, @@ -524,9 +523,9 @@ pub async fn fetch_creature_by_id( pub async fn fetch_creatures_core_data_with_filters( conn: &Pool, - key_value_filters: &HashMap>, + bestiary_filter_query: &BestiaryFilterQuery, ) -> Result> { - let query = prepare_filtered_get_creatures_core(key_value_filters); + let query = prepare_filtered_get_creatures_core(bestiary_filter_query); let core_data: Vec = sqlx::query_as(query.as_str()).fetch_all(conn).await?; Ok(update_creatures_core_with_traits(conn, core_data).await) } diff --git a/src/db/data_providers/raw_query_builder.rs b/src/db/data_providers/raw_query_builder.rs index b6aec3a..5906851 100644 --- a/src/db/data_providers/raw_query_builder.rs +++ b/src/db/data_providers/raw_query_builder.rs @@ -1,10 +1,9 @@ -use crate::models::creature::creature_filter_enum::CreatureFilter; +use crate::models::bestiary_structs::{BestiaryFilterQuery, CreatureTableFieldsFilter}; +use crate::models::creature::creature_metadata::alignment_enum::AlignmentEnum; +use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; use crate::models::item::item_metadata::type_enum::ItemTypeEnum; use crate::models::shop_structs::{ItemTableFieldsFilter, ShopFilterQuery}; use log::debug; -use std::collections::{HashMap, HashSet}; - -const ACCURACY_THRESHOLD: i64 = 50; pub fn prepare_filtered_get_items(shop_filter_query: &ShopFilterQuery) -> String { let equipment_query = prepare_item_subquery( @@ -49,67 +48,26 @@ pub fn prepare_filtered_get_items(shop_filter_query: &ShopFilterQuery) -> String debug!("{}", query); query } -pub fn prepare_filtered_get_creatures_core( - key_value_filters: &HashMap>, -) -> String { - let mut simple_core_query = String::new(); - let mut trait_query = String::new(); - for (key, value) in key_value_filters { - match key { - CreatureFilter::Level - | CreatureFilter::PathfinderVersion - | CreatureFilter::Melee - | CreatureFilter::Ranged - | CreatureFilter::SpellCaster => { - if !simple_core_query.is_empty() { - simple_core_query.push_str(" AND "); - } - simple_core_query.push_str( - prepare_in_statement_for_generic_type(key.to_string().as_str(), value.iter()) - .as_str(), - ); - } - CreatureFilter::Family - | CreatureFilter::Alignment - | CreatureFilter::Size - | CreatureFilter::Rarity - | CreatureFilter::CreatureTypes => { - if !simple_core_query.is_empty() { - simple_core_query.push_str(" AND "); - } - simple_core_query.push_str( - prepare_case_insensitive_in_statement( - key.to_string().as_str(), - value.iter().cloned(), - ) - .as_str(), - ); - } - CreatureFilter::Traits => { - trait_query.push_str(prepare_creature_trait_filter(value.iter().cloned()).as_str()); - } - CreatureFilter::CreatureRoles => { - if !simple_core_query.is_empty() { - simple_core_query.push_str(" AND "); - } - simple_core_query - .push_str(prepare_bounded_or_check(value, ACCURACY_THRESHOLD, 100).as_str()); - } - CreatureFilter::Sources => (), // Never given as value to filter - } - } - let mut where_query = simple_core_query.to_string(); - if !trait_query.is_empty() { - where_query.push_str(format!(" AND id IN ({trait_query}) GROUP BY cc.id").as_str()); - } - if !where_query.is_empty() { - where_query = format!("WHERE {where_query}"); - } +pub fn prepare_filtered_get_creatures_core(bestiary_filter_query: &BestiaryFilterQuery) -> String { + let initial_statement = "SELECT id FROM CREATURE_CORE"; + let trait_query_tmp = prepare_trait_filter_statement( + &prepare_creature_trait_filter(bestiary_filter_query.trait_whitelist_filter.iter()), + &prepare_creature_trait_filter(bestiary_filter_query.trait_blacklist_filter.iter()), + ); + let trait_query = if trait_query_tmp.is_empty() { + String::new() + } else { + format!("AND {trait_query_tmp}") + }; + let creature_fields_filter_query = + prepare_creature_filter_statement(&bestiary_filter_query.creature_table_fields_filter); + let where_query = + format!("{initial_statement} WHERE {creature_fields_filter_query} {trait_query}"); let query = format!( " WITH CreatureRankedByLevel AS ( SELECT *, ROW_NUMBER() OVER (PARTITION BY level ORDER BY RANDOM()) AS rn - FROM CREATURE_CORE cc {where_query} + FROM CREATURE_CORE cc WHERE cc.id IN ({where_query}) ) SELECT * FROM CreatureRankedByLevel WHERE id IN ( SELECT id FROM CreatureRankedByLevel WHERE rn>1 ORDER BY RANDOM() LIMIT 20 @@ -122,24 +80,21 @@ pub fn prepare_filtered_get_creatures_core( query } -/// Prepares a 'bounded OR statement' aka checks if all the columns are in the bound given, ex +/// Prepares a 'bounded OR statement' aka checks if all columns values are in the bound given, ex /// ```SQL -/// (brute_percentage >= 0 AND brute_percentage <= 0) OR (sniper_percentage >= 0 ...) ... +/// (brute_percentage >= 0 AND brute_percentage <= 0) AND (sniper_percentage >= 0 ...) ... /// ``` -fn prepare_bounded_or_check( - column_names: &HashSet, - lower_bound: i64, - upper_bound: i64, -) -> String { - let mut bounded_query = String::new(); - for column in column_names { - if !bounded_query.is_empty() { - bounded_query.push_str(" OR "); - } - bounded_query - .push_str(prepare_bounded_check(column.as_str(), lower_bound, upper_bound).as_str()); - } - bounded_query +fn prepare_bounded_and_check(column_names: I, lower_bound: i64, upper_bound: i64) -> String +where + I: Iterator, + S: ToString, +{ + column_names + .map(|x| x.to_string()) + .filter(|column| !column.is_empty()) + .map(|column| prepare_bounded_check(column.as_str(), lower_bound, upper_bound)) + .collect::>() + .join(" AND ") } /// Prepares a 'bounded statement' aka (x>=lb AND x<=ub) @@ -228,6 +183,7 @@ where } result_string } + fn prepare_item_subquery( item_type: &ItemTypeEnum, n_of_item: i64, @@ -241,9 +197,9 @@ where { let item_type_query = prepare_get_id_matching_item_type_query(item_type); let initial_statement = "SELECT id FROM ITEM_TABLE"; - - let trait_query_tmp = - prepare_trait_filter_statement(trait_whitelist_filter, trait_blacklist_filter); + let whitelist_query = prepare_item_trait_filter(trait_whitelist_filter); + let blacklist_query = prepare_item_trait_filter(trait_blacklist_filter); + let trait_query_tmp = prepare_trait_filter_statement(&whitelist_query, &blacklist_query); let trait_query = if trait_query_tmp.is_empty() { String::new() } else { @@ -287,17 +243,80 @@ fn prepare_item_filter_statement(shop_filter_vectors: &ItemTableFieldsFilter) -> } } +fn prepare_creature_filter_statement( + bestiary_filter_vectors: &CreatureTableFieldsFilter, +) -> String { + let remaster_query = prepare_in_statement_for_generic_type( + "remaster", + bestiary_filter_vectors.supported_version.iter(), + ); + let filters_query = vec![ + prepare_case_insensitive_in_statement( + "source", + bestiary_filter_vectors.source_filter.iter(), + ), + prepare_case_insensitive_in_statement( + "family", + bestiary_filter_vectors.family_filter.iter(), + ), + prepare_case_insensitive_in_statement( + "alignment", + bestiary_filter_vectors + .alignment_filter + .iter() + .flat_map(AlignmentEnum::to_db_traits), + ), + prepare_case_insensitive_in_statement("size", bestiary_filter_vectors.size_filter.iter()), + prepare_case_insensitive_in_statement( + "rarity", + bestiary_filter_vectors.rarity_filter.iter(), + ), + prepare_case_insensitive_in_statement( + "cr_type", + bestiary_filter_vectors.type_filter.iter(), + ), + prepare_in_statement_for_generic_type( + "is_spellcaster", + bestiary_filter_vectors.is_spellcaster_filter.iter(), + ), + prepare_in_statement_for_generic_type( + "is_ranged", + bestiary_filter_vectors.is_ranged_filter.iter(), + ), + prepare_in_statement_for_generic_type( + "is_melee", + bestiary_filter_vectors.is_melee_filter.iter(), + ), + prepare_bounded_and_check( + bestiary_filter_vectors + .role_filter + .iter() + .map(CreatureRoleEnum::to_db_column), + i64::from(bestiary_filter_vectors.role_lower_threshold), + i64::from(bestiary_filter_vectors.role_upper_threshold), + ), + ] + .into_iter() + .filter(|query| !query.is_empty()) + .collect::>() + .join(" AND "); + if filters_query.is_empty() { + remaster_query + } else { + format!("{remaster_query} AND {filters_query}") + } +} + /// Prepares an 'in' statement, with the following logic /// ```SQL /// id NOT IN (bl_id1, bl_id2, bl_idn) AND id IN (wl_id1, wl_id2, wl_idn) /// ``` -fn prepare_trait_filter_statement(whitelist: I, blacklist: I) -> String +fn prepare_trait_filter_statement(whitelist: &S, blacklist: &S) -> String where - I: Iterator, S: ToString, { - let whitelist_query = prepare_item_trait_filter(whitelist); - let blacklist_query = prepare_item_trait_filter(blacklist); + let whitelist_query = whitelist.to_string(); + let blacklist_query = blacklist.to_string(); if whitelist_query.is_empty() && blacklist_query.is_empty() { String::new() } else if whitelist_query.is_empty() { @@ -338,36 +357,36 @@ fn prepare_get_id_matching_item_type_query(item_type: &ItemTypeEnum) -> String { /// Prepares a query that gets all the ids linked with a given list of traits, example /// ```SQL -/// SELECT tcat.creature_id -/// FROM TRAIT_CREATURE_ASSOCIATION_TABLE tcat +/// SELECT tcat.item_id +/// FROM TRAIT_ITEM_ASSOCIATION_TABLE tcat /// RIGHT JOIN /// (SELECT * FROM TRAIT_TABLE WHERE name IN ('good')) tt -/// ON tcat.trait_id = tt.name GROUP BY tcat.creature_id +/// ON tcat.trait_id = tt.name GROUP BY tcat.item_id ///``` -fn prepare_creature_trait_filter(column_values: I) -> String +fn prepare_item_trait_filter(column_values: I) -> String where I: Iterator, S: ToString, { - prepare_trait_filter( - "creature_id", - "TRAIT_CREATURE_ASSOCIATION_TABLE", - column_values, - ) + prepare_trait_filter("item_id", "TRAIT_ITEM_ASSOCIATION_TABLE", column_values) } /// Prepares a query that gets all the ids linked with a given list of traits, example /// ```SQL -/// SELECT tcat.item_id -/// FROM TRAIT_ITEM_ASSOCIATION_TABLE tcat +/// SELECT tcat.creature_id +/// FROM TRAIT_CREATURE_ASSOCIATION_TABLE tcat /// RIGHT JOIN /// (SELECT * FROM TRAIT_TABLE WHERE name IN ('good')) tt -/// ON tcat.trait_id = tt.name GROUP BY tcat.item_id +/// ON tcat.trait_id = tt.name GROUP BY tcat.creature_id ///``` -fn prepare_item_trait_filter(column_values: I) -> String +fn prepare_creature_trait_filter(column_values: I) -> String where I: Iterator, S: ToString, { - prepare_trait_filter("item_id", "TRAIT_ITEM_ASSOCIATION_TABLE", column_values) + prepare_trait_filter( + "creature_id", + "TRAIT_CREATURE_ASSOCIATION_TABLE", + column_values, + ) } diff --git a/src/db/shop_proxy.rs b/src/db/shop_proxy.rs index 2291c57..864df7d 100644 --- a/src/db/shop_proxy.rs +++ b/src/db/shop_proxy.rs @@ -67,7 +67,11 @@ pub async fn get_paginated_items( let curr_slice: Vec = filtered_list .iter() .skip(pagination.paginated_request.cursor as usize) - .take(pagination.paginated_request.page_size.unsigned_abs() as usize) + .take(if pagination.paginated_request.page_size >= 0 { + pagination.paginated_request.page_size.unsigned_abs() as usize + } else { + usize::MAX + }) .cloned() .collect(); diff --git a/src/models/bestiary_structs.rs b/src/models/bestiary_structs.rs index 4ae99b2..5d43997 100644 --- a/src/models/bestiary_structs.rs +++ b/src/models/bestiary_structs.rs @@ -1,4 +1,9 @@ +use crate::models::creature::creature_metadata::alignment_enum::AlignmentEnum; +use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; +use crate::models::creature::creature_metadata::type_enum::CreatureTypeEnum; use crate::models::routers_validator_structs::{OrderEnum, PaginatedRequest}; +use crate::models::shared::rarity_enum::RarityEnum; +use crate::models::shared::size_enum::SizeEnum; use serde::{Deserialize, Serialize}; use strum::Display; use utoipa::{IntoParams, ToSchema}; @@ -37,3 +42,39 @@ pub struct BestiaryPaginatedRequest { pub paginated_request: PaginatedRequest, pub bestiary_sort_data: BestiarySortData, } + +#[derive(Clone)] +pub struct CreatureTableFieldsFilter { + pub source_filter: Vec, + pub family_filter: Vec, + pub alignment_filter: Vec, + pub size_filter: Vec, + pub rarity_filter: Vec, + pub type_filter: Vec, + pub role_filter: Vec, + pub role_lower_threshold: u8, + pub role_upper_threshold: u8, + pub is_melee_filter: Vec, + pub is_ranged_filter: Vec, + pub is_spellcaster_filter: Vec, + pub supported_version: Vec, + + pub level_filter: Vec, +} + +impl CreatureTableFieldsFilter { + pub const fn default_lower_threshold() -> u8 { + 50 + } + + pub const fn default_upper_threshold() -> u8 { + 100 + } +} + +#[derive(Clone)] +pub struct BestiaryFilterQuery { + pub creature_table_fields_filter: CreatureTableFieldsFilter, + pub trait_whitelist_filter: Vec, + pub trait_blacklist_filter: Vec, +} diff --git a/src/models/creature/creature_component/filter_struct.rs b/src/models/creature/creature_component/filter_struct.rs deleted file mode 100644 index 3508484..0000000 --- a/src/models/creature/creature_component/filter_struct.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::models::creature::creature_metadata::alignment_enum::AlignmentEnum; -use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; -use crate::models::creature::creature_metadata::type_enum::CreatureTypeEnum; -use crate::models::pf_version_enum::PathfinderVersionEnum; -use crate::models::shared::rarity_enum::RarityEnum; -use crate::models::shared::size_enum::SizeEnum; -use std::collections::HashSet; - -pub struct FilterStruct { - pub families: Option>, - pub traits: Option>, - pub rarities: Option>, - pub sizes: Option>, - pub alignments: Option>, - pub creature_types: Option>, - pub creature_roles: Option>, - pub lvl_combinations: HashSet, - pub pathfinder_version: PathfinderVersionEnum, -} diff --git a/src/models/creature/creature_component/mod.rs b/src/models/creature/creature_component/mod.rs index 288e9a1..e3692a9 100644 --- a/src/models/creature/creature_component/mod.rs +++ b/src/models/creature/creature_component/mod.rs @@ -3,4 +3,3 @@ pub mod creature_core; pub mod creature_extra; pub mod creature_spell_caster; pub mod creature_variant; -pub mod filter_struct; diff --git a/src/models/creature/creature_metadata/alignment_enum.rs b/src/models/creature/creature_metadata/alignment_enum.rs index 2460a97..8606a00 100644 --- a/src/models/creature/creature_metadata/alignment_enum.rs +++ b/src/models/creature/creature_metadata/alignment_enum.rs @@ -94,6 +94,45 @@ impl From<(&Vec, bool)> for AlignmentEnum { } } +impl AlignmentEnum { + pub fn to_db_traits(&self) -> Vec { + match self { + Self::Ce => { + vec![String::from("CHAOTIC"), String::from("EVIL")] + } + Self::Cn => { + vec![String::from("CHAOTIC")] + } + Self::Cg => { + vec![String::from("CHAOTIC"), String::from("GOOD")] + } + Self::Ne => { + vec![String::from("EVIL")] + } + Self::N | Self::No => { + vec![] + } + Self::Ng => { + vec![String::from("GOOD")] + } + Self::Le => { + vec![String::from("LAWFUL"), String::from("EVIL")] + } + Self::Ln => { + vec![String::from("LAWFUL")] + } + Self::Lg => { + vec![String::from("LAWFUL"), String::from("GOOD")] + } + Self::Any => ALIGNMENT_TRAITS + .to_vec() + .iter() + .map(ToString::to_string) + .collect(), + } + } +} + impl From for AlignmentEnum { fn from(value: String) -> Self { Self::from_str(value.as_str()).unwrap_or_default() diff --git a/src/models/encounter_structs.rs b/src/models/encounter_structs.rs index 337696c..6236b1f 100644 --- a/src/models/encounter_structs.rs +++ b/src/models/encounter_structs.rs @@ -22,13 +22,20 @@ pub struct EncounterParams { #[derive(Serialize, Deserialize, ToSchema)] pub struct RandomEncounterData { - pub families: Option>, - pub traits: Option>, - pub rarities: Option>, - pub sizes: Option>, - pub alignments: Option>, - pub creature_types: Option>, - pub creature_roles: Option>, + pub source_filter: Option>, + pub trait_whitelist_filter: Option>, + pub trait_blacklist_filter: Option>, + pub family_filter: Option>, + pub rarity_filter: Option>, + pub size_filter: Option>, + pub alignment_filter: Option>, + pub type_filter: Option>, + pub role_filter: Option>, + pub is_melee_filter: Option, + pub is_ranged_filter: Option, + pub is_spellcaster_filter: Option, + pub role_lower_threshold: Option, + pub role_upper_threshold: Option, pub challenge: Option, pub adventure_group: Option, #[schema(minimum = 1, maximum = 30, example = 1)] diff --git a/src/models/item/item_fields_enum.rs b/src/models/item/item_fields_enum.rs deleted file mode 100644 index bccbb11..0000000 --- a/src/models/item/item_fields_enum.rs +++ /dev/null @@ -1,37 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Clone)] -pub enum ItemField { - #[serde(alias = "category", alias = "CATEGORY")] - Category, - #[serde(alias = "level", alias = "LEVEL")] - Level, - #[serde(alias = "usage", alias = "USAGE")] - Usage, - #[serde(alias = "item_type", alias = "ITEM_TYPE")] - ItemType, - #[serde(alias = "material_grade", alias = "MATERIAL_GRADE")] - MaterialGrade, - #[serde(alias = "material_type", alias = "MATERIAL_TYPE")] - MaterialType, - #[serde(alias = "number_of_uses", alias = "NUMBER_OF_USES")] - NumberOfUses, - #[serde(alias = "size", alias = "SIZE")] - Size, - #[serde(alias = "rarity", alias = "RARITY")] - Rarity, - #[serde(alias = "sources", alias = "SOURCES")] - Sources, - #[serde(alias = "traits", alias = "TRAITS")] - Traits, -} - -#[derive(Default, Eq, PartialEq, Clone)] -pub struct FieldsUniqueValuesStruct { - pub list_of_levels: Vec, - pub list_of_categories: Vec, - pub list_of_traits: Vec, - pub list_of_sources: Vec, - pub list_of_sizes: Vec, - pub list_of_rarities: Vec, -} diff --git a/src/models/item/mod.rs b/src/models/item/mod.rs index 1896e38..d4d0352 100644 --- a/src/models/item/mod.rs +++ b/src/models/item/mod.rs @@ -1,5 +1,4 @@ pub mod armor_struct; -pub mod item_fields_enum; pub mod item_metadata; pub mod item_struct; pub mod shield_struct; diff --git a/src/services/encounter_service.rs b/src/services/encounter_service.rs index d74cb33..42ae611 100644 --- a/src/services/encounter_service.rs +++ b/src/services/encounter_service.rs @@ -1,7 +1,6 @@ use crate::db::bestiary_proxy::{get_creatures_passing_all_filters, order_list_by_level}; -use crate::models::creature::creature_component::filter_struct::FilterStruct; -use crate::models::creature::creature_filter_enum::CreatureFilter; -use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; +use crate::models::bestiary_structs::BestiaryFilterQuery; +use crate::models::bestiary_structs::CreatureTableFieldsFilter; use crate::models::creature::creature_struct::Creature; use crate::models::encounter_structs::{ AdventureGroupEnum, EncounterChallengeEnum, EncounterParams, RandomEncounterData, @@ -12,11 +11,12 @@ use crate::services::encounter_handler::encounter_calculator::calculate_encounte use crate::AppState; use anyhow::{ensure, Result}; use counter::Counter; +use itertools::Itertools; use log::warn; use serde::{Deserialize, Serialize}; #[allow(unused_imports)] // it's used for Schema use serde_json::json; -use std::collections::{BTreeMap, HashMap, HashSet}; +use std::collections::{BTreeMap, HashSet}; use utoipa::ToSchema; #[derive(Serialize, Deserialize, ToSchema)] @@ -87,27 +87,48 @@ async fn calculate_random_encounter( .clone() .into_iter() .flatten() - .map(|lvl| lvl.to_string()) - .collect::>(); + .sorted() + .dedup() + .collect::>(); ensure!( !unique_levels.is_empty(), "There are no valid levels to chose from. Encounter could not be built" ); - let filter_map = build_filter_map(FilterStruct { - families: enc_data.families, - traits: enc_data.traits, - rarities: enc_data.rarities, - sizes: enc_data.sizes, - alignments: enc_data.alignments, - creature_types: enc_data.creature_types, - creature_roles: enc_data.creature_roles, - lvl_combinations: unique_levels, - pathfinder_version: enc_data.pathfinder_version.unwrap_or_default(), - }); - let filtered_creatures = get_filtered_creatures( app_state, - filter_map, + &BestiaryFilterQuery { + creature_table_fields_filter: CreatureTableFieldsFilter { + source_filter: enc_data.source_filter.unwrap_or_default(), + family_filter: enc_data.family_filter.unwrap_or_default(), + alignment_filter: enc_data.alignment_filter.unwrap_or_default(), + size_filter: enc_data.size_filter.unwrap_or_default(), + rarity_filter: enc_data.rarity_filter.unwrap_or_default(), + type_filter: enc_data.type_filter.unwrap_or_default(), + role_filter: enc_data.role_filter.unwrap_or_default(), + role_lower_threshold: enc_data + .role_lower_threshold + .unwrap_or(CreatureTableFieldsFilter::default_lower_threshold()), + role_upper_threshold: enc_data + .role_upper_threshold + .unwrap_or(CreatureTableFieldsFilter::default_upper_threshold()), + is_melee_filter: enc_data + .is_melee_filter + .map_or_else(|| vec![true, false], |x| vec![x]), + is_ranged_filter: enc_data + .is_ranged_filter + .map_or_else(|| vec![true, false], |x| vec![x]), + is_spellcaster_filter: enc_data + .is_spellcaster_filter + .map_or_else(|| vec![true, false], |x| vec![x]), + supported_version: enc_data + .pathfinder_version + .unwrap_or_default() + .to_db_value(), + level_filter: unique_levels, + }, + trait_whitelist_filter: enc_data.trait_whitelist_filter.unwrap_or_default(), + trait_blacklist_filter: enc_data.trait_blacklist_filter.unwrap_or_default(), + }, enc_data.allow_weak_variants.is_some_and(|x| x), enc_data.allow_elite_variants.is_some_and(|x| x), ) @@ -224,72 +245,13 @@ fn filter_non_existing_levels( result_vec } -fn build_filter_map(filter_enum: FilterStruct) -> HashMap> { - let mut filter_map = HashMap::new(); - - filter_enum - .families - .map(|el| filter_map.insert(CreatureFilter::Family, HashSet::from_iter(el))); - filter_enum - .traits - .map(|el| filter_map.insert(CreatureFilter::Traits, HashSet::from_iter(el))); - // What no generic enum does to a mf (mother function) - // it could also prob be bad programming by me - if let Some(vec) = filter_enum.rarities { - filter_map.insert( - CreatureFilter::Rarity, - vec.iter() - .map(std::string::ToString::to_string) - .collect::>(), - ); - }; - if let Some(vec) = filter_enum.sizes { - filter_map.insert( - CreatureFilter::Size, - vec.iter() - .map(std::string::ToString::to_string) - .collect::>(), - ); - }; - if let Some(vec) = filter_enum.alignments { - filter_map.insert( - CreatureFilter::Alignment, - vec.iter() - .map(std::string::ToString::to_string) - .collect::>(), - ); - }; - if let Some(vec) = filter_enum.creature_types { - filter_map.insert( - CreatureFilter::CreatureTypes, - vec.iter() - .map(std::string::ToString::to_string) - .collect::>(), - ); - }; - if let Some(vec) = filter_enum.creature_roles { - filter_map.insert( - CreatureFilter::CreatureRoles, - vec.iter() - .map(CreatureRoleEnum::to_db_column) - .collect::>(), - ); - }; - filter_map.insert(CreatureFilter::Level, filter_enum.lvl_combinations); - filter_map.insert( - CreatureFilter::PathfinderVersion, - HashSet::from_iter(filter_enum.pathfinder_version.to_db_value()), - ); - filter_map -} - async fn get_filtered_creatures( app_state: &AppState, - filter_map: HashMap>, + filters: &BestiaryFilterQuery, allow_weak: bool, allow_elite: bool, ) -> Result> { - get_creatures_passing_all_filters(app_state, filter_map, allow_weak, allow_elite).await + get_creatures_passing_all_filters(app_state, filters, allow_weak, allow_elite).await } fn get_lvl_combinations(enc_data: &RandomEncounterData, party_levels: &[i64]) -> HashSet> { From ab95c49663695446ffdc41ecffb1dd66969b1d0c Mon Sep 17 00:00:00 2001 From: rakuja Date: Mon, 9 Dec 2024 10:20:28 +0100 Subject: [PATCH 12/18] chore: add sort by is_melee, is_ranged, is_spellcaster --- src/db/bestiary_proxy.rs | 20 ++++++++++++++++++++ src/models/bestiary_structs.rs | 8 ++++++++ 2 files changed, 28 insertions(+) diff --git a/src/db/bestiary_proxy.rs b/src/db/bestiary_proxy.rs index 52c7af5..ce7a86b 100644 --- a/src/db/bestiary_proxy.rs +++ b/src/db/bestiary_proxy.rs @@ -97,6 +97,26 @@ pub async fn get_paginated_creatures( .essential .family .cmp(&b.core_data.essential.family), + CreatureSortEnum::Alignment => a + .core_data + .essential + .alignment + .cmp(&b.core_data.essential.alignment), + CreatureSortEnum::Melee => a + .core_data + .derived + .is_melee + .cmp(&b.core_data.derived.is_melee), + CreatureSortEnum::Ranged => a + .core_data + .derived + .is_ranged + .cmp(&b.core_data.derived.is_ranged), + CreatureSortEnum::SpellCaster => a + .core_data + .derived + .is_spell_caster + .cmp(&b.core_data.derived.is_spell_caster), }; match pagination .bestiary_sort_data diff --git a/src/models/bestiary_structs.rs b/src/models/bestiary_structs.rs index 5d43997..dc8eef2 100644 --- a/src/models/bestiary_structs.rs +++ b/src/models/bestiary_structs.rs @@ -29,6 +29,14 @@ pub enum CreatureSortEnum { Rarity, #[serde(alias = "family", alias = "FAMILY")] Family, + #[serde(alias = "alignment", alias = "ALIGNMENT")] + Alignment, + #[serde(alias = "melee", alias = "MELEE")] + Melee, + #[serde(alias = "ranged", alias = "RANGED")] + Ranged, + #[serde(alias = "SPELLCASTER", alias = "SPELLCASTER")] + SpellCaster, } #[derive(Serialize, Deserialize, IntoParams, ToSchema, Eq, PartialEq, Hash, Default)] From 84d82813b28ec75e068d30f414c8f4211a869be9 Mon Sep 17 00:00:00 2001 From: rakuja Date: Mon, 9 Dec 2024 16:12:38 +0100 Subject: [PATCH 13/18] fix: remove column typos, correctly filter by alignment --- src/db/data_providers/raw_query_builder.rs | 8 +--- .../creature_metadata/alignment_enum.rs | 39 ------------------- 2 files changed, 2 insertions(+), 45 deletions(-) diff --git a/src/db/data_providers/raw_query_builder.rs b/src/db/data_providers/raw_query_builder.rs index 5906851..a6131c4 100644 --- a/src/db/data_providers/raw_query_builder.rs +++ b/src/db/data_providers/raw_query_builder.rs @@ -1,5 +1,4 @@ use crate::models::bestiary_structs::{BestiaryFilterQuery, CreatureTableFieldsFilter}; -use crate::models::creature::creature_metadata::alignment_enum::AlignmentEnum; use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; use crate::models::item::item_metadata::type_enum::ItemTypeEnum; use crate::models::shop_structs::{ItemTableFieldsFilter, ShopFilterQuery}; @@ -261,10 +260,7 @@ fn prepare_creature_filter_statement( ), prepare_case_insensitive_in_statement( "alignment", - bestiary_filter_vectors - .alignment_filter - .iter() - .flat_map(AlignmentEnum::to_db_traits), + bestiary_filter_vectors.alignment_filter.iter(), ), prepare_case_insensitive_in_statement("size", bestiary_filter_vectors.size_filter.iter()), prepare_case_insensitive_in_statement( @@ -276,7 +272,7 @@ fn prepare_creature_filter_statement( bestiary_filter_vectors.type_filter.iter(), ), prepare_in_statement_for_generic_type( - "is_spellcaster", + "is_spell_caster", bestiary_filter_vectors.is_spellcaster_filter.iter(), ), prepare_in_statement_for_generic_type( diff --git a/src/models/creature/creature_metadata/alignment_enum.rs b/src/models/creature/creature_metadata/alignment_enum.rs index 8606a00..2460a97 100644 --- a/src/models/creature/creature_metadata/alignment_enum.rs +++ b/src/models/creature/creature_metadata/alignment_enum.rs @@ -94,45 +94,6 @@ impl From<(&Vec, bool)> for AlignmentEnum { } } -impl AlignmentEnum { - pub fn to_db_traits(&self) -> Vec { - match self { - Self::Ce => { - vec![String::from("CHAOTIC"), String::from("EVIL")] - } - Self::Cn => { - vec![String::from("CHAOTIC")] - } - Self::Cg => { - vec![String::from("CHAOTIC"), String::from("GOOD")] - } - Self::Ne => { - vec![String::from("EVIL")] - } - Self::N | Self::No => { - vec![] - } - Self::Ng => { - vec![String::from("GOOD")] - } - Self::Le => { - vec![String::from("LAWFUL"), String::from("EVIL")] - } - Self::Ln => { - vec![String::from("LAWFUL")] - } - Self::Lg => { - vec![String::from("LAWFUL"), String::from("GOOD")] - } - Self::Any => ALIGNMENT_TRAITS - .to_vec() - .iter() - .map(ToString::to_string) - .collect(), - } - } -} - impl From for AlignmentEnum { fn from(value: String) -> Self { Self::from_str(value.as_str()).unwrap_or_default() From 2039b8261ae61756c6f5f4d362cdfa821b66f75e Mon Sep 17 00:00:00 2001 From: rakuja Date: Mon, 9 Dec 2024 17:09:05 +0100 Subject: [PATCH 14/18] chore: remove unused "ANY" option from alignment --- src/models/creature/creature_metadata/alignment_enum.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/models/creature/creature_metadata/alignment_enum.rs b/src/models/creature/creature_metadata/alignment_enum.rs index 2460a97..17829e0 100644 --- a/src/models/creature/creature_metadata/alignment_enum.rs +++ b/src/models/creature/creature_metadata/alignment_enum.rs @@ -48,9 +48,6 @@ pub enum AlignmentEnum { #[serde(rename = "No Alignment")] #[default] No, // no alignment - #[strum(to_string = "Any")] - #[serde(rename = "Any")] - Any, // can be every alignment } pub const ALIGNMENT_TRAITS: [&str; 4] = ["GOOD", "EVIL", "CHAOTIC", "LAWFUL"]; @@ -119,7 +116,6 @@ impl FromStr for AlignmentEnum { "LE" => Ok(Self::Le), "LN" => Ok(Self::Ln), "LG" => Ok(Self::Lg), - "ANY" => Ok(Self::Any), _ => Ok(Self::No), } } @@ -138,7 +134,6 @@ impl Clone for AlignmentEnum { Self::Ln => Self::Ln, Self::Lg => Self::Lg, Self::No => Self::No, - Self::Any => Self::Any, } } } From a5f251a2919cfa33a8e8458ebd5436254de7c395 Mon Sep 17 00:00:00 2001 From: rakuja Date: Tue, 10 Dec 2024 19:35:32 +0100 Subject: [PATCH 15/18] chore: bump version --- Cargo.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index fcd5995..45574ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bybe" -version = "2.3.0" +version = "2.4.0" authors = ["RakuJa"] # Compiler info @@ -54,11 +54,11 @@ utoipa-swagger-ui = { version = "8.0.3", features = ["actix-web", "reqwest"] } sqlx = { version = "0.8.2", features = ["runtime-async-std", "sqlite"] } cached = { version = "0.54.0", features = ["async"] } -anyhow = "1.0.93" +anyhow = "1.0.94" serde = { version = "1.0.215", features = ["derive"] } serde_json = "1.0.133" strum = {version="0.26.3", features = ["derive"]} -fastrand = "2.2.0" +fastrand = "2.3.0" counter = "0.6.0" ordered-float = { version = "4", features = ["serde"]} num-traits = "0.2.19" @@ -76,7 +76,7 @@ once = "0.3.4" [build-dependencies] tokio = { version = "1", features = ["macros", "rt-multi-thread", "rt"] } -anyhow = "1.0.93" +anyhow = "1.0.94" sqlx = {version = "0.8.2", features = ["runtime-async-std", "sqlite"]} dotenvy = "0.15.7" From 6e80064308f02f2fddbcda9a0372eff3ec1d30f6 Mon Sep 17 00:00:00 2001 From: rakuja Date: Fri, 13 Dec 2024 09:49:35 +0100 Subject: [PATCH 16/18] feat: provide sort by attacks (melee, ranged, spellcaster) and roles with threshold --- Cargo.toml | 2 +- src/db/bestiary_proxy.rs | 35 ++++--- src/models/bestiary_structs.rs | 10 +- .../creature_component/creature_core.rs | 62 +++++++------ src/models/creature/creature_struct.rs | 91 ++++++++++++------- src/models/encounter_structs.rs | 5 +- src/models/routers_validator_structs.rs | 9 +- src/services/encounter_service.rs | 21 +++-- src/services/url_calculator.rs | 9 -- 9 files changed, 136 insertions(+), 108 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 45574ab..4c75063 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,7 @@ sqlx = { version = "0.8.2", features = ["runtime-async-std", "sqlite"] } cached = { version = "0.54.0", features = ["async"] } anyhow = "1.0.94" -serde = { version = "1.0.215", features = ["derive"] } +serde = { version = "1.0.216", features = ["derive"] } serde_json = "1.0.133" strum = {version="0.26.3", features = ["derive"]} fastrand = "2.3.0" diff --git a/src/db/bestiary_proxy.rs b/src/db/bestiary_proxy.rs index ce7a86b..dcc4fd7 100644 --- a/src/db/bestiary_proxy.rs +++ b/src/db/bestiary_proxy.rs @@ -1,5 +1,5 @@ use crate::models::creature::creature_struct::Creature; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; use crate::db::data_providers::creature_fetcher::fetch_traits_associated_with_creatures; use crate::db::data_providers::{creature_fetcher, generic_fetcher}; @@ -102,21 +102,28 @@ pub async fn get_paginated_creatures( .essential .alignment .cmp(&b.core_data.essential.alignment), - CreatureSortEnum::Melee => a + CreatureSortEnum::Attacks => a .core_data .derived - .is_melee - .cmp(&b.core_data.derived.is_melee), - CreatureSortEnum::Ranged => a - .core_data - .derived - .is_ranged - .cmp(&b.core_data.derived.is_ranged), - CreatureSortEnum::SpellCaster => a - .core_data - .derived - .is_spell_caster - .cmp(&b.core_data.derived.is_spell_caster), + .attack_data + .cmp(&b.core_data.derived.attack_data), + CreatureSortEnum::Roles => { + let threshold = filters.role_threshold.unwrap_or(0); + a.core_data + .derived + .role_data + .iter() + .filter(|(_, y)| **y > threshold) + .collect::>() + .cmp( + &b.core_data + .derived + .role_data + .iter() + .filter(|(_, y)| **y > threshold) + .collect::>(), + ) + } }; match pagination .bestiary_sort_data diff --git a/src/models/bestiary_structs.rs b/src/models/bestiary_structs.rs index dc8eef2..8828a26 100644 --- a/src/models/bestiary_structs.rs +++ b/src/models/bestiary_structs.rs @@ -31,12 +31,10 @@ pub enum CreatureSortEnum { Family, #[serde(alias = "alignment", alias = "ALIGNMENT")] Alignment, - #[serde(alias = "melee", alias = "MELEE")] - Melee, - #[serde(alias = "ranged", alias = "RANGED")] - Ranged, - #[serde(alias = "SPELLCASTER", alias = "SPELLCASTER")] - SpellCaster, + #[serde(alias = "attacks", alias = "ATTACKS")] + Attacks, + #[serde(alias = "roles", alias = "ROLES")] + Roles, } #[derive(Serialize, Deserialize, IntoParams, ToSchema, Eq, PartialEq, Hash, Default)] diff --git a/src/models/creature/creature_component/creature_core.rs b/src/models/creature/creature_component/creature_core.rs index 6820b3a..8035c7d 100644 --- a/src/models/creature/creature_component/creature_core.rs +++ b/src/models/creature/creature_component/creature_core.rs @@ -3,8 +3,11 @@ use crate::models::creature::creature_metadata::type_enum::CreatureTypeEnum; use crate::models::shared::rarity_enum::RarityEnum; use crate::models::shared::size_enum::SizeEnum; use serde::{Deserialize, Serialize}; +#[allow(unused_imports)] +use serde_json::json; use sqlx::sqlite::SqliteRow; use sqlx::{Error, FromRow, Row}; +use std::collections::BTreeMap; use utoipa::ToSchema; #[derive(Serialize, Deserialize, Clone, ToSchema, Eq, Hash, PartialEq)] @@ -36,24 +39,10 @@ pub struct EssentialData { pub struct DerivedData { pub archive_link: Option, - pub is_melee: bool, - pub is_ranged: bool, - pub is_spell_caster: bool, - - #[schema(example = 50)] - pub brute_percentage: i64, - #[schema(example = 50)] - pub magical_striker_percentage: i64, - #[schema(example = 50)] - pub skill_paragon_percentage: i64, - #[schema(example = 50)] - pub skirmisher_percentage: i64, - #[schema(example = 50)] - pub sniper_percentage: i64, - #[schema(example = 50)] - pub soldier_percentage: i64, - #[schema(example = 50)] - pub spell_caster_percentage: i64, + #[schema(example = json!({"melee": true, "ranged": false, "spellcaster": true}))] + pub attack_data: BTreeMap, + #[schema(example = json!({"brute": 50, "magical_striker": 30, "skill_paragon": 2, "skirmisher": 3, "sniper": 0, "soldier": 30, "spellcaster": 90}))] + pub role_data: BTreeMap, } impl<'r> FromRow<'r, SqliteRow> for EssentialData { @@ -81,18 +70,35 @@ impl<'r> FromRow<'r, SqliteRow> for EssentialData { impl<'r> FromRow<'r, SqliteRow> for DerivedData { fn from_row(row: &'r SqliteRow) -> Result { + let mut attack_list = BTreeMap::new(); + attack_list.insert(String::from("melee"), row.try_get("is_melee")?); + attack_list.insert(String::from("ranged"), row.try_get("is_ranged")?); + attack_list.insert(String::from("spellcaster"), row.try_get("is_spell_caster")?); + + let mut role_list = BTreeMap::new(); + role_list.insert(String::from("brute"), row.try_get("brute_percentage")?); + role_list.insert( + String::from("magical_striker"), + row.try_get("magical_striker_percentage")?, + ); + role_list.insert( + String::from("skill_paragon"), + row.try_get("skill_paragon_percentage")?, + ); + role_list.insert( + String::from("skirmisher"), + row.try_get("skirmisher_percentage")?, + ); + role_list.insert(String::from("sniper"), row.try_get("sniper_percentage")?); + role_list.insert(String::from("soldier"), row.try_get("soldier_percentage")?); + role_list.insert( + String::from("spellcaster"), + row.try_get("spell_caster_percentage")?, + ); Ok(Self { archive_link: row.try_get("archive_link").ok(), - is_melee: row.try_get("is_melee")?, - is_ranged: row.try_get("is_ranged")?, - is_spell_caster: row.try_get("is_spell_caster")?, - brute_percentage: row.try_get("brute_percentage")?, - magical_striker_percentage: row.try_get("magical_striker_percentage")?, - skill_paragon_percentage: row.try_get("skill_paragon_percentage")?, - skirmisher_percentage: row.try_get("skirmisher_percentage")?, - sniper_percentage: row.try_get("sniper_percentage")?, - soldier_percentage: row.try_get("soldier_percentage")?, - spell_caster_percentage: row.try_get("spell_caster_percentage")?, + attack_data: attack_list, + role_data: role_list, }) } } diff --git a/src/models/creature/creature_struct.rs b/src/models/creature/creature_struct.rs index d2c091a..14c3391 100644 --- a/src/models/creature/creature_struct.rs +++ b/src/models/creature/creature_struct.rs @@ -128,42 +128,63 @@ impl Creature { }) && filters.alignment_filter.as_ref().map_or(true, |x| { x.iter() .any(|align| self.core_data.essential.alignment == *align) - }) && filters - .is_melee_filter - .map_or(true, |is_melee| self.core_data.derived.is_melee == is_melee) - && filters.is_ranged_filter.map_or(true, |is_ranged| { - self.core_data.derived.is_ranged == is_ranged - }) - && filters - .is_spell_caster_filter - .map_or(true, |is_spell_caster| { - self.core_data.derived.is_spell_caster == is_spell_caster + }) && filters.attack_data_filter.clone().map_or(true, |attacks| { + self.core_data.derived.attack_data == attacks + }) && filters.type_filter.as_ref().map_or(true, |x| { + x.iter() + .any(|cr_type| self.core_data.essential.cr_type == *cr_type) + }) && (filters.role_threshold.is_none() + || filters.role_filter.as_ref().map_or(true, |cr_role| { + let t = filters.role_threshold.unwrap_or(0); + cr_role.iter().any(|role| match role { + CreatureRoleEnum::Brute => { + self.core_data.derived.role_data.get("brute").unwrap_or(&0) >= &t + } + CreatureRoleEnum::MagicalStriker => { + self.core_data + .derived + .role_data + .get("magical_striker") + .unwrap_or(&0) + >= &t + } + CreatureRoleEnum::SkillParagon => { + self.core_data + .derived + .role_data + .get("skill_paragon") + .unwrap_or(&0) + >= &t + } + CreatureRoleEnum::Skirmisher => { + self.core_data + .derived + .role_data + .get("skirmisher") + .unwrap_or(&0) + >= &t + } + CreatureRoleEnum::Sniper => { + self.core_data.derived.role_data.get("sniper").unwrap_or(&0) >= &t + } + CreatureRoleEnum::Soldier => { + self.core_data + .derived + .role_data + .get("soldier") + .unwrap_or(&0) + >= &t + } + CreatureRoleEnum::SpellCaster => { + self.core_data + .derived + .role_data + .get("spellcaster") + .unwrap_or(&0) + >= &t + } }) - && filters.type_filter.as_ref().map_or(true, |x| { - x.iter() - .any(|cr_type| self.core_data.essential.cr_type == *cr_type) - }) - && (filters.role_threshold.is_none() - || filters.role_filter.as_ref().map_or(true, |cr_role| { - let t = filters.role_threshold.unwrap_or(0); - cr_role.iter().any(|role| match role { - CreatureRoleEnum::Brute => self.core_data.derived.brute_percentage >= t, - CreatureRoleEnum::MagicalStriker => { - self.core_data.derived.magical_striker_percentage >= t - } - CreatureRoleEnum::SkillParagon => { - self.core_data.derived.skill_paragon_percentage >= t - } - CreatureRoleEnum::Skirmisher => { - self.core_data.derived.skirmisher_percentage >= t - } - CreatureRoleEnum::Sniper => self.core_data.derived.sniper_percentage >= t, - CreatureRoleEnum::Soldier => self.core_data.derived.soldier_percentage >= t, - CreatureRoleEnum::SpellCaster => { - self.core_data.derived.spell_caster_percentage >= t - } - }) - })) + })) && match filters.pathfinder_version.clone().unwrap_or_default() { PathfinderVersionEnum::Legacy => !self.core_data.essential.remaster, PathfinderVersionEnum::Remaster => self.core_data.essential.remaster, diff --git a/src/models/encounter_structs.rs b/src/models/encounter_structs.rs index 6236b1f..2a7444c 100644 --- a/src/models/encounter_structs.rs +++ b/src/models/encounter_structs.rs @@ -7,6 +7,7 @@ use crate::models::shared::size_enum::SizeEnum; use serde::{Deserialize, Serialize}; #[allow(unused_imports)] // Used in schema use serde_json::json; +use std::collections::HashMap; use strum::EnumCount; use strum::EnumIter; use utoipa::ToSchema; @@ -31,9 +32,7 @@ pub struct RandomEncounterData { pub alignment_filter: Option>, pub type_filter: Option>, pub role_filter: Option>, - pub is_melee_filter: Option, - pub is_ranged_filter: Option, - pub is_spellcaster_filter: Option, + pub attack_list: Option>, pub role_lower_threshold: Option, pub role_upper_threshold: Option, pub challenge: Option, diff --git a/src/models/routers_validator_structs.rs b/src/models/routers_validator_structs.rs index 7832fd8..a40aaf9 100644 --- a/src/models/routers_validator_structs.rs +++ b/src/models/routers_validator_structs.rs @@ -6,6 +6,9 @@ use crate::models::pf_version_enum::PathfinderVersionEnum; use crate::models::shared::rarity_enum::RarityEnum; use crate::models::shared::size_enum::SizeEnum; use serde::{Deserialize, Serialize}; +#[allow(unused_imports)] +use serde_json::json; +use std::collections::BTreeMap; use strum::Display; use utoipa::{IntoParams, ToSchema}; #[derive(Serialize, Deserialize, IntoParams, ToSchema)] @@ -30,9 +33,9 @@ pub struct CreatureFieldFilters { pub min_level_filter: Option, #[schema(minimum = -1, example = 5)] pub max_level_filter: Option, - pub is_melee_filter: Option, - pub is_ranged_filter: Option, - pub is_spell_caster_filter: Option, + + #[schema(example = json!({"melee": true, "ranged": false, "spellcaster": true}))] + pub attack_data_filter: Option>, pub pathfinder_version: Option, } diff --git a/src/services/encounter_service.rs b/src/services/encounter_service.rs index 42ae611..21ef7ed 100644 --- a/src/services/encounter_service.rs +++ b/src/services/encounter_service.rs @@ -111,15 +111,18 @@ async fn calculate_random_encounter( role_upper_threshold: enc_data .role_upper_threshold .unwrap_or(CreatureTableFieldsFilter::default_upper_threshold()), - is_melee_filter: enc_data - .is_melee_filter - .map_or_else(|| vec![true, false], |x| vec![x]), - is_ranged_filter: enc_data - .is_ranged_filter - .map_or_else(|| vec![true, false], |x| vec![x]), - is_spellcaster_filter: enc_data - .is_spellcaster_filter - .map_or_else(|| vec![true, false], |x| vec![x]), + is_melee_filter: enc_data.attack_list.as_ref().map_or_else( + || vec![true, false], + |x| vec![*x.get("melee").unwrap_or(&false)], + ), + is_ranged_filter: enc_data.attack_list.as_ref().map_or_else( + || vec![true, false], + |x| vec![*x.get("ranged").unwrap_or(&false)], + ), + is_spellcaster_filter: enc_data.attack_list.map_or_else( + || vec![true, false], + |x| vec![*x.get("spellcaster").unwrap_or(&false)], + ), supported_version: enc_data .pathfinder_version .unwrap_or_default() diff --git a/src/services/url_calculator.rs b/src/services/url_calculator.rs index db46249..d6d67c2 100644 --- a/src/services/url_calculator.rs +++ b/src/services/url_calculator.rs @@ -80,15 +80,6 @@ fn creature_filter_query_calculator(field_filters: &CreatureFieldFilters) -> Str field_filters .max_level_filter .map(|lvl| format!("max_level_filter={lvl}")), - field_filters - .is_melee_filter - .map(|is| format!("is_melee_filter={is}")), - field_filters - .is_ranged_filter - .map(|is| format!("is_ranged_filter={is}")), - field_filters - .is_spell_caster_filter - .map(|is| format!("is_spell_caster_filter={is}")), ] .iter() .filter_map(std::clone::Clone::clone) From 36d5dae1af5a62b9203e16ece6d591c4965a33e5 Mon Sep 17 00:00:00 2001 From: rakuja Date: Sat, 14 Dec 2024 17:04:26 +0100 Subject: [PATCH 17/18] chore: rename SpellCaster into Spellcaster --- src/db/cr_core_initializer.rs | 2 +- src/models/creature/creature_metadata/creature_role.rs | 10 +++++----- src/models/creature/creature_struct.rs | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/db/cr_core_initializer.rs b/src/db/cr_core_initializer.rs index 690ddb1..fc5b758 100644 --- a/src/db/cr_core_initializer.rs +++ b/src/db/cr_core_initializer.rs @@ -104,7 +104,7 @@ async fn update_role_column_value( creature_id ) } - CreatureRoleEnum::SpellCaster => { + CreatureRoleEnum::Spellcaster => { sqlx::query!( "UPDATE CREATURE_CORE SET spell_caster_percentage = ? WHERE id = ?", value, diff --git a/src/models/creature/creature_metadata/creature_role.rs b/src/models/creature/creature_metadata/creature_role.rs index 9e1ffa9..418a96f 100644 --- a/src/models/creature/creature_metadata/creature_role.rs +++ b/src/models/creature/creature_metadata/creature_role.rs @@ -28,7 +28,7 @@ pub enum CreatureRoleEnum { Skirmisher, Sniper, Soldier, - SpellCaster, + Spellcaster, } fn get_dmg_from_regex(raw_str: &str) -> Option { @@ -46,7 +46,7 @@ impl CreatureRoleEnum { Self::Skirmisher => String::from("skirmisher_percentage"), Self::Sniper => String::from("sniper_percentage"), Self::Soldier => String::from("soldier_percentage"), - Self::SpellCaster => String::from("spell_caster_percentage"), + Self::Spellcaster => String::from("spell_caster_percentage"), } } pub fn from_creature_with_given_scales( @@ -87,7 +87,7 @@ impl CreatureRoleEnum { .map_or(0, |x| (x * 100.).round() as i64), ); roles.insert( - Self::SpellCaster, + Self::Spellcaster, is_spellcaster(cr_core, cr_spells, cr_combat, cr_extra, scales) .map_or(0, |x| (x * 100.).round() as i64), ); @@ -448,7 +448,7 @@ impl FromStr for CreatureRoleEnum { "SKIRMISHER" => Ok(Self::Skirmisher), "SNIPER" => Ok(Self::Sniper), "SOLDIER" => Ok(Self::Soldier), - "SPELLCASTER" | "SPELL CASTER" => Ok(Self::SpellCaster), + "SPELLCASTER" | "SPELL CASTER" => Ok(Self::Spellcaster), _ => Err(()), } } @@ -475,7 +475,7 @@ impl fmt::Display for CreatureRoleEnum { Self::Soldier => { write!(f, "Soldier") } - Self::SpellCaster => { + Self::Spellcaster => { write!(f, "SpellCaster") } } diff --git a/src/models/creature/creature_struct.rs b/src/models/creature/creature_struct.rs index 14c3391..d100ff3 100644 --- a/src/models/creature/creature_struct.rs +++ b/src/models/creature/creature_struct.rs @@ -175,7 +175,7 @@ impl Creature { .unwrap_or(&0) >= &t } - CreatureRoleEnum::SpellCaster => { + CreatureRoleEnum::Spellcaster => { self.core_data .derived .role_data From 2f2a124d8164697c20ae9c625cd24a54c2eb72c6 Mon Sep 17 00:00:00 2001 From: rakuja Date: Sat, 14 Dec 2024 17:33:16 +0100 Subject: [PATCH 18/18] fix: wrong sorting of role (remove value, move to only name) * rename some roles * change every variant of SpellCaster into Spellcaster --- src/db/bestiary_proxy.rs | 18 ++++++++++-------- src/db/data_providers/creature_fetcher.rs | 12 ++++++------ src/models/bestiary_structs.rs | 8 ++++---- .../creature_spell_caster.rs | 8 ++++---- src/models/creature/creature_filter_enum.rs | 4 ++-- .../creature_metadata/creature_role.rs | 10 +++++----- src/models/creature/creature_struct.rs | 4 ++-- .../creature/items/spell_caster_entry.rs | 2 +- src/models/response_data.rs | 4 ++-- src/routes/bestiary.rs | 8 ++++---- 10 files changed, 40 insertions(+), 38 deletions(-) diff --git a/src/db/bestiary_proxy.rs b/src/db/bestiary_proxy.rs index dcc4fd7..426f076 100644 --- a/src/db/bestiary_proxy.rs +++ b/src/db/bestiary_proxy.rs @@ -1,5 +1,5 @@ use crate::models::creature::creature_struct::Creature; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; use crate::db::data_providers::creature_fetcher::fetch_traits_associated_with_creatures; use crate::db::data_providers::{creature_fetcher, generic_fetcher}; @@ -102,26 +102,28 @@ pub async fn get_paginated_creatures( .essential .alignment .cmp(&b.core_data.essential.alignment), - CreatureSortEnum::Attacks => a + CreatureSortEnum::Attack => a .core_data .derived .attack_data .cmp(&b.core_data.derived.attack_data), - CreatureSortEnum::Roles => { + CreatureSortEnum::Role => { let threshold = filters.role_threshold.unwrap_or(0); a.core_data .derived .role_data .iter() - .filter(|(_, y)| **y > threshold) - .collect::>() + .filter(|(_, role_value)| **role_value > threshold) + .map(|(role, _)| role) + .collect::>() .cmp( &b.core_data .derived .role_data .iter() - .filter(|(_, y)| **y > threshold) - .collect::>(), + .filter(|(_, role_affinity)| **role_affinity > threshold) + .map(|(x, _)| x) + .collect::>(), ) } }; @@ -203,7 +205,7 @@ pub async fn get_all_possible_values_of_filter( let mut x = match field { CreatureFilter::Size => runtime_fields_values.list_of_sizes, CreatureFilter::Rarity => runtime_fields_values.list_of_rarities, - CreatureFilter::Ranged | CreatureFilter::Melee | CreatureFilter::SpellCaster => { + CreatureFilter::Ranged | CreatureFilter::Melee | CreatureFilter::Spellcaster => { vec![true.to_string(), false.to_string()] } CreatureFilter::Family => runtime_fields_values.list_of_families, diff --git a/src/db/data_providers/creature_fetcher.rs b/src/db/data_providers/creature_fetcher.rs index c46ebeb..101bf20 100644 --- a/src/db/data_providers/creature_fetcher.rs +++ b/src/db/data_providers/creature_fetcher.rs @@ -11,7 +11,7 @@ use crate::models::creature::creature_component::creature_core::CreatureCoreData use crate::models::creature::creature_component::creature_extra::{ AbilityScores, CreatureExtraData, }; -use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellCasterData; +use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellcasterData; use crate::models::creature::creature_component::creature_variant::CreatureVariantData; use crate::models::creature::creature_metadata::alignment_enum::ALIGNMENT_TRAITS; use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; @@ -19,7 +19,7 @@ use crate::models::creature::creature_struct::Creature; use crate::models::creature::items::action::Action; use crate::models::creature::items::skill::Skill; use crate::models::creature::items::spell::Spell; -use crate::models::creature::items::spell_caster_entry::SpellCasterEntry; +use crate::models::creature::items::spell_caster_entry::SpellcasterEntry; use crate::models::db::raw_immunity::RawImmunity; use crate::models::db::raw_language::RawLanguage; use crate::models::db::raw_resistance::RawResistance; @@ -422,9 +422,9 @@ async fn fetch_creature_spells(conn: &Pool, creature_id: i64) -> Result< async fn fetch_creature_spell_caster_entry( conn: &Pool, creature_id: i64, -) -> Result { +) -> Result { Ok(sqlx::query_as!( - SpellCasterEntry, + SpellcasterEntry, "SELECT spell_casting_name, is_spell_casting_flexible, type_of_spell_caster, spell_casting_dc_mod, spell_casting_atk_mod, spell_casting_tradition FROM CREATURE_TABLE WHERE id == ($1) LIMIT 1", creature_id ).fetch_one(conn).await?) @@ -641,10 +641,10 @@ pub async fn fetch_creature_combat_data( pub async fn fetch_creature_spell_caster_data( conn: &Pool, creature_id: i64, -) -> Result { +) -> Result { let spells = fetch_creature_spells(conn, creature_id).await?; let spell_caster_entry = fetch_creature_spell_caster_entry(conn, creature_id).await?; - Ok(CreatureSpellCasterData { + Ok(CreatureSpellcasterData { spells, spell_caster_entry, }) diff --git a/src/models/bestiary_structs.rs b/src/models/bestiary_structs.rs index 8828a26..4566dbd 100644 --- a/src/models/bestiary_structs.rs +++ b/src/models/bestiary_structs.rs @@ -31,10 +31,10 @@ pub enum CreatureSortEnum { Family, #[serde(alias = "alignment", alias = "ALIGNMENT")] Alignment, - #[serde(alias = "attacks", alias = "ATTACKS")] - Attacks, - #[serde(alias = "roles", alias = "ROLES")] - Roles, + #[serde(alias = "attack", alias = "ATTACK")] + Attack, + #[serde(alias = "role", alias = "ROLE")] + Role, } #[derive(Serialize, Deserialize, IntoParams, ToSchema, Eq, PartialEq, Hash, Default)] diff --git a/src/models/creature/creature_component/creature_spell_caster.rs b/src/models/creature/creature_component/creature_spell_caster.rs index 1f2b46e..f5932ef 100644 --- a/src/models/creature/creature_component/creature_spell_caster.rs +++ b/src/models/creature/creature_component/creature_spell_caster.rs @@ -1,16 +1,16 @@ use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; use crate::models::creature::items::spell::Spell; -use crate::models::creature::items::spell_caster_entry::SpellCasterEntry; +use crate::models::creature::items::spell_caster_entry::SpellcasterEntry; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; #[derive(Serialize, Deserialize, Clone, ToSchema, Eq, Hash, PartialEq)] -pub struct CreatureSpellCasterData { +pub struct CreatureSpellcasterData { pub spells: Vec, - pub spell_caster_entry: SpellCasterEntry, + pub spell_caster_entry: SpellcasterEntry, } -impl CreatureSpellCasterData { +impl CreatureSpellcasterData { pub fn add_mod_to_spellcaster_atk_and_dc(self, modifier: i64) -> Self { let mut spell_data = self; diff --git a/src/models/creature/creature_filter_enum.rs b/src/models/creature/creature_filter_enum.rs index 6a2b21e..55eb9a5 100644 --- a/src/models/creature/creature_filter_enum.rs +++ b/src/models/creature/creature_filter_enum.rs @@ -18,7 +18,7 @@ pub enum CreatureFilter { #[serde(alias = "is_ranged", alias = "IS_RANGED")] Ranged, #[serde(alias = "is_spell_caster", alias = "IS_SPELL_CASTER")] - SpellCaster, + Spellcaster, #[serde(alias = "sources", alias = "SOURCES")] Sources, #[serde(alias = "traits", alias = "TRAITS")] @@ -51,7 +51,7 @@ impl fmt::Display for CreatureFilter { Self::Ranged => { write!(f, "is_ranged") } - Self::SpellCaster => { + Self::Spellcaster => { write!(f, "is_spell_caster") } Self::Traits => { diff --git a/src/models/creature/creature_metadata/creature_role.rs b/src/models/creature/creature_metadata/creature_role.rs index 418a96f..d1e7836 100644 --- a/src/models/creature/creature_metadata/creature_role.rs +++ b/src/models/creature/creature_metadata/creature_role.rs @@ -1,7 +1,7 @@ use crate::models::creature::creature_component::creature_combat::CreatureCombatData; use crate::models::creature::creature_component::creature_core::EssentialData; use crate::models::creature::creature_component::creature_extra::CreatureExtraData; -use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellCasterData; +use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellcasterData; use crate::models::item::item_metadata::type_enum::WeaponTypeEnum; use crate::models::scales_struct::creature_scales::CreatureScales; use num_traits::float::FloatConst; @@ -53,7 +53,7 @@ impl CreatureRoleEnum { cr_core: &EssentialData, cr_extra: &CreatureExtraData, cr_combat: &CreatureCombatData, - cr_spells: &CreatureSpellCasterData, + cr_spells: &CreatureSpellcasterData, scales: &CreatureScales, ) -> BTreeMap { let mut roles = BTreeMap::new(); @@ -293,7 +293,7 @@ pub fn is_soldier( // Magical Striker pub fn is_magical_striker( cr_core: &EssentialData, - cr_spell: &CreatureSpellCasterData, + cr_spell: &CreatureSpellcasterData, cr_combat: &CreatureCombatData, scales: &CreatureScales, ) -> Option { @@ -395,7 +395,7 @@ fn is_skill_paragon( // Spellcaster fn is_spellcaster( cr_core: &EssentialData, - cr_spell: &CreatureSpellCasterData, + cr_spell: &CreatureSpellcasterData, cr_combat: &CreatureCombatData, cr_extra: &CreatureExtraData, scales: &CreatureScales, @@ -476,7 +476,7 @@ impl fmt::Display for CreatureRoleEnum { write!(f, "Soldier") } Self::Spellcaster => { - write!(f, "SpellCaster") + write!(f, "Spellcaster") } } } diff --git a/src/models/creature/creature_struct.rs b/src/models/creature/creature_struct.rs index d100ff3..db160b2 100644 --- a/src/models/creature/creature_struct.rs +++ b/src/models/creature/creature_struct.rs @@ -1,7 +1,7 @@ use crate::models::creature::creature_component::creature_combat::CreatureCombatData; use crate::models::creature::creature_component::creature_core::CreatureCoreData; use crate::models::creature::creature_component::creature_extra::CreatureExtraData; -use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellCasterData; +use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellcasterData; use crate::models::creature::creature_component::creature_variant::CreatureVariantData; use crate::models::creature::creature_metadata::creature_role::CreatureRoleEnum; use crate::models::creature::creature_metadata::variant_enum::CreatureVariant; @@ -15,7 +15,7 @@ pub struct Creature { pub variant_data: CreatureVariantData, pub extra_data: Option, pub combat_data: Option, - pub spell_caster_data: Option, + pub spell_caster_data: Option, } impl Creature { diff --git a/src/models/creature/items/spell_caster_entry.rs b/src/models/creature/items/spell_caster_entry.rs index 52dd38d..c719f18 100644 --- a/src/models/creature/items/spell_caster_entry.rs +++ b/src/models/creature/items/spell_caster_entry.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use utoipa::ToSchema; #[derive(Serialize, Deserialize, Clone, ToSchema, Eq, Hash, PartialEq)] -pub struct SpellCasterEntry { +pub struct SpellcasterEntry { pub spell_casting_name: Option, pub is_spell_casting_flexible: Option, pub type_of_spell_caster: Option, diff --git a/src/models/response_data.rs b/src/models/response_data.rs index 908a87c..6abd7aa 100644 --- a/src/models/response_data.rs +++ b/src/models/response_data.rs @@ -1,7 +1,7 @@ use crate::models::creature::creature_component::creature_combat::CreatureCombatData; use crate::models::creature::creature_component::creature_core::CreatureCoreData; use crate::models::creature::creature_component::creature_extra::CreatureExtraData; -use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellCasterData; +use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellcasterData; use crate::models::creature::creature_component::creature_variant::CreatureVariantData; use crate::models::creature::creature_struct::Creature; use crate::models::item::armor_struct::ArmorData; @@ -25,7 +25,7 @@ pub struct ResponseCreature { pub variant_data: CreatureVariantData, pub extra_data: Option, pub combat_data: Option, - pub spell_caster_data: Option, + pub spell_caster_data: Option, } impl From for ResponseCreature { diff --git a/src/routes/bestiary.rs b/src/routes/bestiary.rs index ecc8f2b..9506cc8 100644 --- a/src/routes/bestiary.rs +++ b/src/routes/bestiary.rs @@ -16,14 +16,14 @@ use crate::models::creature::creature_component::creature_core::DerivedData; use crate::models::creature::creature_component::creature_core::EssentialData; use crate::models::creature::creature_component::creature_extra::AbilityScores; use crate::models::creature::creature_component::creature_extra::CreatureExtraData; -use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellCasterData; +use crate::models::creature::creature_component::creature_spell_caster::CreatureSpellcasterData; use crate::models::creature::creature_component::creature_variant::CreatureVariantData; use crate::models::pf_version_enum::PathfinderVersionEnum; use crate::models::creature::items::action::Action; use crate::models::creature::items::skill::Skill; use crate::models::creature::items::spell::Spell; -use crate::models::creature::items::spell_caster_entry::SpellCasterEntry; +use crate::models::creature::items::spell_caster_entry::SpellcasterEntry; use crate::models::item::armor_struct::Armor; use crate::models::item::weapon_struct::Weapon; @@ -90,7 +90,7 @@ pub fn init_docs(doc: &mut utoipa::openapi::OpenApi) { CreatureVariantData, CreatureExtraData, CreatureCombatData, - CreatureSpellCasterData, + CreatureSpellcasterData, Sense, Spell, Shield, @@ -101,7 +101,7 @@ pub fn init_docs(doc: &mut utoipa::openapi::OpenApi) { Action, Skill, CreatureRoleEnum, - SpellCasterEntry, + SpellcasterEntry, PathfinderVersionEnum, OrderEnum, CreatureSortEnum