Files
siren/src/db/spells/model.rs
2023-10-04 13:09:04 -04:00

291 lines
11 KiB
Rust

use diesel::prelude::*;
use serde::{Deserialize, Serialize};
use crate::{db::{schema::spells::{self}, classes::AbilityType, conditions::ConditionType}, error_handler::ServiceError};
use super::{SchoolType, CastingTime, CastingType, SpellAttackType, SpellDamageType, Range, Area, Components, Duration, Source, Description, DurationType};
#[derive(Queryable, QueryableByName)]
#[diesel(table_name = spells)]
pub struct QuerySpell {
pub id: i32,
pub name: String,
pub school: String,
pub level: i32,
pub ritual: bool,
pub concentration: bool,
pub classes: Vec<String>,
pub damage_inflict: Vec<String>,
pub damage_resist: Vec<String>,
pub conditions: Vec<String>,
pub saving_throw: Vec<String>,
pub attack_type: Option<String>,
pub data: serde_json::Value,
}
#[derive(Debug)]
pub struct QueryFilters {
pub by_name: Option<String>,
pub by_schools: Option<Vec<String>>,
pub by_levels: Option<Vec<i32>>,
pub by_ritual: Option<bool>,
pub by_concentration: Option<bool>,
pub by_classes: Option<Vec<String>>,
pub by_damage_inflict: Option<Vec<String>>,
pub by_damage_resist: Option<Vec<String>>,
pub by_conditions: Option<Vec<String>>,
pub by_saving_throw: Option<Vec<String>>,
pub by_attack_type: Option<String>,
}
impl Default for QueryFilters {
fn default() -> Self {
Self {
by_name: None,
by_schools: None,
by_levels: None,
by_ritual: None,
by_concentration: None,
by_classes: None,
by_damage_inflict: None,
by_damage_resist: None,
by_conditions: None,
by_saving_throw: None,
by_attack_type: None,
}
}
}
impl QuerySpell {
pub fn get_all(filters: &QueryFilters, limit: i32, page: i32) -> Result<Vec<Self>, ServiceError> {
let mut conn = crate::db::connection()?;
let mut query = spells::table.limit(limit as i64).into_boxed();
// Limit query to page and limit
let offset = (page - 1) * limit;
query = query.offset(offset as i64);
if let Some(name) = &filters.by_name {
query = query.filter(spells::name.ilike(format!("%{}%", name)));
}
if let Some(schools) = &filters.by_schools {
query = query.filter(spells::school.eq_any(schools.iter().map(|school| school.to_string()).collect::<Vec<String>>()));
}
if let Some(levels) = &filters.by_levels {
query = query.filter(spells::level.eq_any(levels));
}
if let Some(ritual) = filters.by_ritual {
query = query.filter(spells::ritual.eq(ritual));
}
if let Some(concentration) = filters.by_concentration {
query = query.filter(spells::concentration.eq(concentration));
}
if let Some(classes) = &filters.by_classes {
query = query.filter(spells::classes.overlaps_with(classes));
}
if let Some(damage_inflict) = &filters.by_damage_inflict {
query = query.filter(spells::damage_inflict.overlaps_with(damage_inflict.iter().map(|damage_inflict| damage_inflict.to_string()).collect::<Vec<String>>()));
}
if let Some(damage_resist) = &filters.by_damage_resist {
query = query.filter(spells::damage_resist.overlaps_with(damage_resist.iter().map(|damage_resist| damage_resist.to_string()).collect::<Vec<String>>()));
}
if let Some(conditions) = &filters.by_conditions {
query = query.filter(spells::conditions.overlaps_with(conditions.iter().map(|condition| condition.to_string()).collect::<Vec<String>>()));
}
if let Some(saving_throw) = &filters.by_saving_throw {
query = query.filter(spells::saving_throw.overlaps_with(saving_throw.iter().map(|saving_throw| saving_throw.to_string()).collect::<Vec<String>>()));
}
if let Some(attack_type) = &filters.by_attack_type {
query = query.filter(spells::attack_type.eq(attack_type.to_string()));
}
let spells = query.load::<QuerySpell>(&mut conn)?;
Ok(spells)
}
pub fn get_count(filters: &QueryFilters) -> Result<i64, ServiceError> {
let mut conn = crate::db::connection()?;
let mut query = spells::table.count().into_boxed();
if let Some(name) = &filters.by_name {
query = query.filter(spells::name.ilike(format!("%{}%", name)));
}
if let Some(schools) = &filters.by_schools {
query = query.filter(spells::school.eq_any(schools.iter().map(|school| school.to_string()).collect::<Vec<String>>()));
}
if let Some(levels) = &filters.by_levels {
query = query.filter(spells::level.eq_any(levels));
}
if let Some(ritual) = filters.by_ritual {
query = query.filter(spells::ritual.eq(ritual));
}
if let Some(concentration) = filters.by_concentration {
query = query.filter(spells::concentration.eq(concentration));
}
if let Some(classes) = &filters.by_classes {
query = query.filter(spells::classes.overlaps_with(classes));
}
if let Some(damage_inflict) = &filters.by_damage_inflict {
query = query.filter(spells::damage_inflict.overlaps_with(damage_inflict.iter().map(|damage_inflict| damage_inflict.to_string()).collect::<Vec<String>>()));
}
if let Some(damage_resist) = &filters.by_damage_resist {
query = query.filter(spells::damage_resist.overlaps_with(damage_resist.iter().map(|damage_resist| damage_resist.to_string()).collect::<Vec<String>>()));
}
if let Some(conditions) = &filters.by_conditions {
query = query.filter(spells::conditions.overlaps_with(conditions.iter().map(|condition| condition.to_string()).collect::<Vec<String>>()));
}
if let Some(saving_throw) = &filters.by_saving_throw {
query = query.filter(spells::saving_throw.overlaps_with(saving_throw.iter().map(|saving_throw| saving_throw.to_string()).collect::<Vec<String>>()));
}
if let Some(attack_type) = &filters.by_attack_type {
query = query.filter(spells::attack_type.eq(attack_type.to_string()));
}
let count = query.get_result(&mut conn)?;
Ok(count)
}
pub fn get_by_id(id: i32) -> Result<Self, ServiceError> {
let mut conn = crate::db::connection()?;
let spell = spells::table
.filter(spells::id.eq(id))
.first::<QuerySpell>(&mut conn)?;
Ok(spell)
}
pub fn delete(id: i32) -> Result<Self, ServiceError> {
let mut conn = crate::db::connection()?;
let spell = diesel::delete(spells::table.filter(spells::id.eq(id))).get_result(&mut conn)?;
Ok(spell)
}
}
#[derive(Insertable, AsChangeset)]
#[diesel(table_name = spells)]
pub struct InsertSpell {
pub name: String,
pub school: String,
pub level: i32,
pub ritual: bool,
pub concentration: bool,
pub classes: Vec<String>,
pub damage_inflict: Vec<String>,
pub damage_resist: Vec<String>,
pub conditions: Vec<String>,
pub saving_throw: Vec<String>,
pub attack_type: Option<String>,
pub data: serde_json::Value
}
impl InsertSpell {
pub fn insert(spell: Self) -> Result<QuerySpell, ServiceError> {
let mut conn = crate::db::connection()?;
let spell = diesel::insert_into(spells::table).values(spell).get_result(&mut conn)?;
Ok(spell)
}
pub fn update(id: i32, spell: Self) -> Result<QuerySpell, ServiceError> {
let mut conn = crate::db::connection()?;
let spell = diesel::update(spells::table.filter(spells::id.eq(id))).set(spell).get_result(&mut conn)?;
Ok(spell)
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Spell {
pub name: String,
pub school: SchoolType,
pub level: i32,
pub ritual: bool,
pub casting_time: CastingTime,
#[serde(skip_serializing_if = "Option::is_none")]
pub saving_throw: Option<Vec<AbilityType>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub attack_type: Option<SpellAttackType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub damage_inflict: Option<Vec<SpellDamageType>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub damage_resist: Option<Vec<SpellDamageType>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub conditions: Option<Vec<ConditionType>>,
pub range: Range,
#[serde(skip_serializing_if = "Option::is_none")]
pub area: Option<Area>,
pub components: Components,
pub durations: Vec<Duration>,
pub classes: Vec<String>,
pub sources: Vec<Source>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<Description>
}
impl From<QuerySpell> for Spell {
fn from(query: QuerySpell) -> Self {
return match serde_json::from_value(query.data) {
Ok(data) => data,
Err(err) => {
log::error!("Failed to parse spell: {}", err);
Self {
name: "".to_string(),
school: SchoolType::Abjuration,
level: 0,
ritual: false,
casting_time: CastingTime { amount: 0, casting_type: CastingType::Action },
saving_throw: None,
attack_type: None,
damage_inflict: None,
damage_resist: None,
conditions: None,
range: Range { range_type: "".to_string(), amount: None, unit: None },
area: None,
components: Components { verbal: false, somatic: false, material: false, materials_needed: None, materials_cost: None, materials_consumed: None },
durations: vec![],
classes: vec![],
sources: vec![],
tags: None,
description: None,
}
}
}
}
}
impl Into<InsertSpell> for Spell {
fn into(self) -> InsertSpell {
return InsertSpell {
name: self.name.to_string(),
school: self.school.to_string(),
level: self.level,
ritual: self.ritual,
concentration: self.durations.iter().any(|duration| match duration.duration_type {
DurationType::Concentration => true,
_ => false
}),
classes: self.classes.iter().map(|class| class.to_string()).collect::<Vec<String>>(),
damage_inflict: match &self.damage_inflict {
Some(damage_inflict) => damage_inflict.iter().map(|damage_inflict| damage_inflict.to_string()).collect(),
None => vec![]
},
damage_resist: match &self.damage_resist {
Some(damage_resist) => damage_resist.iter().map(|damage_resist| damage_resist.to_string()).collect(),
None => vec![]
},
conditions: match &self.conditions {
Some(conditions) => conditions.iter().map(|condition| condition.to_string()).collect(),
None => vec![]
},
saving_throw: match &self.saving_throw {
Some(saving_throw) => saving_throw.iter().map(|saving_throw| saving_throw.to_string()).collect(),
None => vec![]
},
attack_type: self.attack_type.as_ref().map(|attack_type| attack_type.to_string()),
data: match serde_json::to_value(&self) {
Ok(data) => data,
Err(err) => {
log::error!("Failed to serialize spell: {}", err);
serde_json::Value::Null
}
}
}
}
}