use crate::db::{Dao, TenebrousDbConn}; use crate::errors::Error; use crate::models::characters::{Character, CharacterDataType, DynCharacterData, Visibility}; use crate::models::proto::cofd::*; use crate::models::users::User; use rocket_contrib::templates::Template; use serde::Serialize; use std::borrow::Cow; use std::collections::btree_map::{Entry, OccupiedEntry}; pub(crate) fn routes() -> Vec { routes![ cofd::update_basic_info, cofd::update_attributes, cofd::update_attribute, cofd::update_skills, cofd::update_skill_value, cofd::add_condition, cofd::remove_condition ] } /// Load the character belonging to the given user, as long as they're /// the owner of that character. Returns an error if user is not /// logged in, the owner of the character is not found, or the logged /// in user does not have the permission to access this character. async fn load_character( conn: &TenebrousDbConn<'_>, logged_in_user: Option<&User>, owner: &str, character_id: i32, ) -> Result { let logged_in_user = logged_in_user.ok_or(Error::NotLoggedIn)?; let character: Character = conn .load_character(character_id) .await? .ok_or(Error::NotFound)?; if &logged_in_user.username != owner { return Err(Error::NoPermission); } Ok(character) } /// Protobuf-based REST endpoints for editing a character. mod cofd { use super::*; use crate::models::proto::cofd::cofd_sheet::Skill; use crate::models::proto::{cofd::api::*, cofd::*, Proto}; fn find_skill_entry<'a>( sheet: &'a mut CofdSheet, skill_name: &'a str, ) -> Option> { let all_skills = vec![ &mut sheet.mental_skills, &mut sheet.physical_skills, &mut sheet.social_skills, ]; // Search all skill lists for this value using "workaround" to // break value from for loops. let skill: Option> = 'l: loop { for skill_map in all_skills { if let Entry::Occupied(entry) = skill_map.entry(skill_name.to_owned()) { break 'l Some(entry); } } break None; }; skill } fn find_skill<'a>(sheet: &'a mut CofdSheet, skill_name: &'a str) -> Option<&'a mut Skill> { find_skill_entry(sheet, skill_name).map(|entry| entry.into_mut()) } #[post("/cofd///basic-info", data = "")] pub(super) fn update_basic_info<'a>( owner: String, character_id: i32, info: Proto, ) -> &'a str { "lol" } #[post("/cofd///attributes", data = "")] pub(super) fn update_attributes<'a>( owner: String, character_id: i32, info: Proto, ) -> &'a str { "lol" } #[patch("/cofd///attributes", data = "")] pub(super) async fn update_attribute<'a>( owner: String, character_id: i32, attr_update: Proto, conn: TenebrousDbConn<'_>, logged_in_user: Option<&User>, ) -> Result<&'a str, Error> { let mut character = load_character(&conn, logged_in_user, &owner, character_id).await?; let mut sheet: CofdSheet = character.try_deserialize()?; match attr_update.name.to_lowercase().as_ref() { "strength" => Ok(sheet.strength = attr_update.value), "dexterity" => Ok(sheet.dexterity = attr_update.value), "stamina" => Ok(sheet.stamina = attr_update.value), "intelligence" => Ok(sheet.intelligence = attr_update.value), "wits" => Ok(sheet.wits = attr_update.value), "resolve" => Ok(sheet.resolve = attr_update.value), "presence" => Ok(sheet.presence = attr_update.value), "manipulation" => Ok(sheet.manipulation = attr_update.value), "composure" => Ok(sheet.composure = attr_update.value), _ => Err(Error::InvalidInput), }?; println!( "updated {} attribute {} to {}", character.character_name, attr_update.name, attr_update.value ); character.update_data(sheet)?; conn.update_character_sheet(&character).await?; Ok("lol") } #[patch( "/cofd///skills", data = "", rank = 1 )] pub(super) async fn update_skills<'a>( owner: String, character_id: i32, skill_update: Proto, conn: TenebrousDbConn<'_>, logged_in_user: Option<&User>, ) -> Result<&'a str, Error> { let mut character = load_character(&conn, logged_in_user, &owner, character_id).await?; let mut sheet: CofdSheet = character.try_deserialize()?; let updated_skill: &Skill = skill_update.skill.as_ref().ok_or(Error::InvalidInput)?; let skill_entry = find_skill_entry(&mut sheet, &skill_update.name); skill_entry .map(|mut entry| entry.insert(updated_skill.clone())) .ok_or(Error::InvalidInput)?; println!( "updated skill {} with {:?}", skill_update.name, skill_update.skill ); character.update_data(sheet)?; conn.update_character_sheet(&character).await?; Ok("lol") } #[post("/rpc/cofd/update_skill_value", data = "")] pub(super) async fn update_skill_value<'a>( request: Proto, conn: TenebrousDbConn<'_>, logged_in_user: Option<&User>, ) -> Result<&'a str, Error> { println!("{:#?}", request); let mut character = load_character( &conn, logged_in_user, &request.character_username, request.character_id, ) .await?; let mut sheet: CofdSheet = character.try_deserialize()?; let skill: Option<&mut Skill> = find_skill(&mut sheet, &request.skill_name); skill .map(|s| s.dots = request.skill_value) .ok_or(Error::InvalidInput)?; println!("updated skill value",); character.update_data(sheet)?; conn.update_character_sheet(&character).await?; Ok("lol") } #[put("/cofd///conditions", data = "")] pub(super) fn add_condition<'a>( owner: String, character_id: i32, info: Proto, ) -> &'a str { "lol" } #[delete("/cofd///conditions", data = "")] pub(super) fn remove_condition<'a>( owner: String, character_id: i32, info: Proto, ) -> &'a str { "lol" } }