291 lines
11 KiB
Rust
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
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|