diff --git a/Cargo.lock b/Cargo.lock index bdac6bc..7994578 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -380,6 +380,15 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" +[[package]] +name = "erased-serde" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ca8b296792113e1500fd935ae487be6e00ce318952a6880555554824d6ebf38" +dependencies = [ + "serde", +] + [[package]] name = "error-chain" version = "0.12.4" @@ -1449,6 +1458,7 @@ version = "0.1.0" dependencies = [ "diesel", "diesel-derive-enum", + "erased-serde", "log 0.4.11", "prost", "prost-build", diff --git a/Cargo.toml b/Cargo.toml index 999d3ff..14bb83c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ prost = "0.6" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" +erased-serde = "0.3" diesel = "1.4" diesel-derive-enum = { version = "1", features = ["sqlite"] } thiserror = "1.0" diff --git a/src/models/characters.rs b/src/models/characters.rs index 89285fc..f0c2ae0 100644 --- a/src/models/characters.rs +++ b/src/models/characters.rs @@ -1,8 +1,17 @@ +use crate::errors::Error; +use crate::models::proto::cofd::*; use crate::models::users::User; use crate::schema::characters; use diesel_derive_enum::DbEnum; use serde_derive::Serialize; +/// Dynamic character data is an opaque container type that holds +/// successfully deserialized character data protobuf object of any +/// type. It does not know what kind of type it has. This is a +/// semantically more appropriate name for what is returned from the +/// dyn_deserialize function. +pub(crate) type DynCharacterData = dyn erased_serde::Serialize; + /// Control system visibility of a character for a particular user. /// Implemented as a trait because there are multiple character /// structs that need this. @@ -59,6 +68,32 @@ impl Visibility for Character { } } +impl Character { + /// Attempt to deserialize the character's data into the given + /// type, which must be one of the protobuf types. + pub fn try_deserialize(&self) -> Result + where + T: prost::Message + std::default::Default, + { + let decoded = T::decode(self.data.as_ref())?; + Ok(decoded) + } + + /// Attempt to deserialize the character's data based on its + /// stored type, but return the deserialized protobuf type as a + /// trait object. Primarily used for passing character sheets to + /// templates or other places (like a REST API). + pub fn dyn_deserialize(&self) -> Result, Error> { + use CharacterDataType::*; + let decoded: Box = match self.data_type { + ChroniclesOfDarknessV1 => Box::new(self.try_deserialize::()?), + ChangelingV1 => Box::new(self.try_deserialize::()?), + }; + + Ok(decoded) + } +} + #[derive(Serialize, Debug, Queryable)] pub struct StrippedCharacter { pub id: i32, diff --git a/src/routes/characters.rs b/src/routes/characters.rs index 10aa2de..5e2644d 100644 --- a/src/routes/characters.rs +++ b/src/routes/characters.rs @@ -1,10 +1,11 @@ use crate::db::{Dao, TenebrousDbConn}; use crate::errors::Error; -use crate::models::characters::Visibility; +use crate::models::characters::{CharacterDataType, DynCharacterData, Visibility}; use crate::models::users::User; use rocket::request::Form; use rocket::response::Redirect; use rocket_contrib::templates::Template; +use serde::Serialize; use std::collections::HashMap; pub(crate) fn routes() -> Vec { @@ -23,10 +24,10 @@ struct NewCharacterForm { } #[derive(Serialize)] -struct ViewCharacterTemlate<'a, T> { +struct ViewCharacterTemplate<'a> { pub name: &'a str, pub username: &'a str, - pub sheet: T, + pub sheet: Box, } #[get("//")] @@ -43,15 +44,10 @@ fn view_character( .and_then(|c| c.as_visible_for(logged_in_user)) .ok_or(Error::NotFound)?; - //TODO determine sheet type and deserialize based on that. - use crate::models::proto::cofd::CofdSheet; - use prost::Message; - let sheet = CofdSheet::decode(character.data.as_ref())?; - - let context = ViewCharacterTemlate { + let context = ViewCharacterTemplate { name: &character.character_name, username: &user.username, - sheet: sheet, + sheet: character.dyn_deserialize()?, }; Ok(Template::render("view_character", context)) @@ -77,7 +73,8 @@ fn create_new_character( use prost::bytes::BytesMut; use prost::Message; - let new_character = CofdSheet::default(); + let mut new_character = CofdSheet::default(); + new_character.strength = 100; let mut buf = BytesMut::with_capacity(std::mem::size_of_val(&new_character)); new_character.encode(&mut buf)?; @@ -85,7 +82,7 @@ fn create_new_character( user_id: logged_in_user.id, viewable: true, character_name: &form.name, - data_type: std::any::type_name::(), + data_type: CharacterDataType::ChroniclesOfDarknessV1, data_version: 1, data: &buf, };