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}; #[derive(Queryable, QueryableByName)] #[diesel(table_name = spells)] pub struct QuerySpell { pub id: i32, pub name: String, pub data: serde_json::Value, } #[derive(Debug)] pub struct QueryFilters { pub by_name: Option, pub by_schools: Option>, pub by_levels: Option>, pub by_ritual: Option, pub by_concentration: Option, pub by_classes: Option>, pub by_damage_inflict: Option>, pub by_damage_resist: Option>, pub by_conditions: Option>, pub by_saving_throw: Option>, pub by_attack_type: Option, } 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, ServiceError> { let mut conn = crate::db::connection()?; let mut query = spells::table.limit(limit as i64).into_boxed(); query = query.filter(spells::id.gt(std::cmp::max(0, page - 1) * limit)); if let Some(name) = filters.by_name.to_owned() { query = query.filter(spells::name.ilike(format!("%{}%", name))); } let spells = query.load::(&mut conn)?; Ok(spells) } pub fn get_by_id(id: i32) -> Result { let mut conn = crate::db::connection()?; let spell = spells::table .filter(spells::id.eq(id)) .first::(&mut conn)?; Ok(spell) } pub fn get_count(filters: &QueryFilters) -> Result { let mut conn = crate::db::connection()?; let mut query = spells::table.count().into_boxed(); if let Some(name) = filters.by_name.to_owned() { query = query.filter(spells::name.ilike(format!("%{}%", name))); } let count = query.get_result(&mut conn)?; Ok(count) } pub fn delete(id: i32) -> Result { 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 data: serde_json::Value } impl InsertSpell { pub fn insert(spell: Self) -> Result { 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 { 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>, #[serde(skip_serializing_if = "Option::is_none")] pub attack_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub damage_inflict: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub damage_resist: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub conditions: Option>, pub range: Range, #[serde(skip_serializing_if = "Option::is_none")] pub area: Option, pub components: Components, pub durations: Vec, pub classes: Vec, pub sources: Vec, #[serde(skip_serializing_if = "Option::is_none")] pub tags: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub description: Option } impl From 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 for Spell { fn into(self) -> InsertSpell { return InsertSpell { name: self.name.to_string(), data: match serde_json::to_value(&self) { Ok(data) => data, Err(err) => { log::error!("Failed to serialize spell description: {}", err); serde_json::Value::Null } }, } } }