use crate::db::{Dao, TenebrousDbConn}; use crate::errors::Error; use crate::models::{ characters::{CharacterDataType, NewCharacter}, convert::ValidationError, users::User, }; use rocket::{request::Form, response::Redirect}; use rocket_contrib::templates::Template; use serde::Serialize; use strum::IntoEnumIterator; /// Form submission for creating a new character. #[derive(FromForm, Serialize)] pub(super) struct NewCharacterForm { name: String, system: Result, } /// Template context for the new character page. Serves as both a /// means of populating form fields and reindering error messages. #[derive(Serialize)] pub(super) struct NewCharacterContext { name: String, error_message: String, selected_system: CharacterDataType, systems: Vec, } impl NewCharacterContext { /// Default empty context, used when page first loads. pub fn default() -> NewCharacterContext { NewCharacterContext { name: "".to_string(), error_message: "".to_string(), selected_system: CharacterDataType::ChroniclesOfDarkness, systems: CharacterDataType::iter().collect(), } } /// Create a context from a form submission. Used to repopulate /// fields in the form when an error is sent back. pub fn from_form(form: &Form) -> NewCharacterContext { let system: CharacterDataType = *form .system .as_ref() .unwrap_or(&CharacterDataType::ChroniclesOfDarkness); NewCharacterContext { name: form.name.clone(), error_message: "".to_string(), selected_system: system, systems: CharacterDataType::iter().collect(), } } } /// Create and insert a new character into the database. The form is /// assumed to be successfully validated. async fn create_new_character( form: &Form, user_id: i32, conn: TenebrousDbConn<'_>, ) -> Result<(), Error> { let system: CharacterDataType = *form.system.as_ref().map_err(|_| Error::InvalidInput)?; let sheet: Vec = system.default_serialized_data()?.to_vec(); let insert = NewCharacter { user_id: user_id, viewable: true, character_name: &form.name, data_type: system, data_version: 1, data: &sheet, }; conn.insert_character(insert).await?; Ok(()) } /// Render an error message on the new character page, repopulating /// the form based on information from the passed-in form. fn render_error(form: &Form, error: String) -> Template { let mut context = NewCharacterContext::from_form(form); context.error_message = error; Template::render("characters/new_character", context) } #[get("/new")] pub(super) fn new_character_page(_logged_in_user: &User) -> Result { Ok(Template::render( "characters/new_character", NewCharacterContext::default(), )) } #[post("/new", data = "
")] pub(super) async fn new_character_submit( form: Form, logged_in_user: &User, conn: TenebrousDbConn<'_>, ) -> Result { if let Err(e) = &form.system { return Err(render_error(&form, e.to_string().clone())); } match create_new_character(&form, logged_in_user.id, conn).await { Ok(_) => Ok(crate::routes::common::redirect_to_index()), Err(e) => Err(render_error(&form, e.to_string().clone())), } } #[get("/new", rank = 2)] pub(super) fn new_character_not_logged_in() -> Redirect { crate::routes::common::redirect_to_login() }