tenebrous-sheets/src/routes/characters/new.rs

117 lines
3.6 KiB
Rust

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<CharacterDataType, ValidationError>,
}
/// 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<CharacterDataType>,
}
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<NewCharacterForm>) -> 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<NewCharacterForm>,
user_id: i32,
conn: TenebrousDbConn<'_>,
) -> Result<(), Error> {
let system: CharacterDataType = *form.system.as_ref().map_err(|_| Error::InvalidInput)?;
let sheet: Vec<u8> = 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<NewCharacterForm>, 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<Template, Error> {
Ok(Template::render(
"characters/new_character",
NewCharacterContext::default(),
))
}
#[post("/new", data = "<form>")]
pub(super) async fn new_character_submit(
form: Form<NewCharacterForm>,
logged_in_user: &User,
conn: TenebrousDbConn<'_>,
) -> Result<Redirect, Template> {
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()
}