use actix_web::{get, post, put, delete, web, HttpResponse, HttpRequest, ResponseError}; use log::error; use serde::{Serialize, Deserialize}; use siren::{GetResponse, Metadata, ServiceError}; use crate::db::spells::{QuerySpell, QueryFilters}; use super::{Spell, InsertSpell}; #[derive(Serialize, Deserialize)] struct GetAllParams { name: Option, like_name: Option, schools: Option, levels: Option, ritual: Option, concentration: Option, classes: Option, damage_inflict: Option, damage_resist: Option, conditions: Option, saving_throw: Option, attack_type: Option, limit: Option, page: Option, } #[get("/spells")] async fn get_all(req: HttpRequest) -> HttpResponse { let params = match web::Query::::from_query(req.query_string()) { Ok(params) => params, Err(err) => return ResponseError::error_response(&ServiceError { status: 422, message: err.to_string() }) }; let mut filters = QueryFilters::default(); filters.by_name = params.name.clone(); filters.like_name = params.like_name.clone(); filters.by_schools = match ¶ms.schools { Some(schools) => Some(schools.split(",").map(|s| s.to_string()).collect()), None => None }; filters.by_levels = match ¶ms.levels { Some(levels) => Some(levels.split(",").map(|s| match s.to_string().parse::() { Ok(level) => level, Err(_) => 0 }).collect()), None => None }; filters.by_ritual = params.ritual; filters.by_concentration = params.concentration; filters.by_classes = match ¶ms.classes { Some(classes) => Some(classes.split(",").map(|s| s.to_string()).collect()), None => None }; filters.by_damage_inflict = match ¶ms.damage_inflict { Some(damage_inflict) => Some(damage_inflict.split(",").map(|s| s.to_string()).collect()), None => None }; filters.by_damage_resist = match ¶ms.damage_resist { Some(damage_resist) => Some(damage_resist.split(",").map(|s| s.to_string()).collect()), None => None }; filters.by_conditions = match ¶ms.conditions { Some(conditions) => Some(conditions.split(",").map(|s| s.to_string()).collect()), None => None }; filters.by_saving_throw = match ¶ms.saving_throw { Some(saving_throw) => Some(saving_throw.split(",").map(|s| s.to_string()).collect()), None => None }; filters.by_attack_type = match ¶ms.attack_type { Some(attack_type) => Some(attack_type.split(",").map(|s| s.to_string()).collect()), None => None }; // Limit must be between 1 and 100 let limit = std::cmp::min(std::cmp::max(params.limit.unwrap_or(100), 1), 100); let total_count = QuerySpell::get_count(&filters).unwrap(); let max_page = std::cmp::max((total_count as f64 / limit as f64).ceil() as i32, 1); // Page must be between 1 and max_page let page = std::cmp::min(std::cmp::max(params.page.unwrap_or(1), 1), max_page); match web::block(move || QuerySpell::get_all(&filters, limit, page)).await.unwrap() { Ok(spells) => { let mut response: Vec = Vec::new(); for query_spell in spells { let id = query_spell.id; let mut spell = Spell::from(query_spell); spell.id = Some(id); response.push(spell); } HttpResponse::Ok().json(GetResponse { data: response, metadata: Some(Metadata { total: total_count as i32, limit, page, pages: max_page }) }) }, Err(err) => { error!("{:?}", err.message); ResponseError::error_response(&err) } } } #[get("/spells/{id}")] async fn get_by_id(id: web::Path) -> HttpResponse { let id = match id.parse::() { Ok(id) => id, Err(err) => return ResponseError::error_response(&ServiceError { status: 422, message: err.to_string() }) }; match web::block(move || QuerySpell::get_by_id(id)).await.unwrap() { Ok(query_spell) => { let id = query_spell.id; let mut spell = Spell::from(query_spell); spell.id = Some(id); HttpResponse::Ok().json(GetResponse { data: spell, metadata: None }) }, Err(err) => { error!("{:?}", err.message); ResponseError::error_response(&err) } } } #[post("/spells")] async fn create(spell: web::Json) -> HttpResponse { match InsertSpell::insert(spell.into_inner().into()) { Ok(spell) => HttpResponse::Created().json(Spell::from(spell)), Err(err) => { error!("{:?}", err.message); ResponseError::error_response(&err) } } } #[put("/spells/{id}")] async fn update(id: web::Path, spell: web::Json) -> HttpResponse { let id = match id.parse::() { Ok(id) => id, Err(err) => return ResponseError::error_response(&ServiceError { status: 422, message: err.to_string() }) }; match web::block(move || InsertSpell::update(id, spell.into_inner().into())).await.unwrap() { Ok(spell) => HttpResponse::Ok().json(Spell::from(spell)), Err(err) => { error!("{:?}", err.message); ResponseError::error_response(&err) } } } #[delete("/spells/{id}")] async fn delete(id: web::Path) -> HttpResponse { let id = match id.parse::() { Ok(id) => id, Err(err) => return ResponseError::error_response(&ServiceError { status: 422, message: err.to_string() }) }; match web::block(move || QuerySpell::delete(id)).await.unwrap() { Ok(spell) => HttpResponse::Ok().json(Spell::from(spell)), Err(err) => { error!("{:?}", err.message); ResponseError::error_response(&err) } } } pub fn init_routes(config: &mut web::ServiceConfig) { config.service(get_all); config.service(get_by_id); config.service(create); config.service(delete); }