Dynamically deserialize character data.

This commit is contained in:
projectmoon 2020-12-08 21:47:01 +00:00
parent 245bd4f2cc
commit 1f07856e66
4 changed files with 55 additions and 12 deletions

10
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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<T>(&self) -> Result<T, Error>
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<Box<DynCharacterData>, Error> {
use CharacterDataType::*;
let decoded: Box<dyn erased_serde::Serialize> = match self.data_type {
ChroniclesOfDarknessV1 => Box::new(self.try_deserialize::<CofdSheet>()?),
ChangelingV1 => Box::new(self.try_deserialize::<ChangelingSheet>()?),
};
Ok(decoded)
}
}
#[derive(Serialize, Debug, Queryable)]
pub struct StrippedCharacter {
pub id: i32,

View File

@ -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<rocket::Route> {
@ -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<DynCharacterData>,
}
#[get("/<username>/<character_id>")]
@ -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::<CofdSheet>(),
data_type: CharacterDataType::ChroniclesOfDarknessV1,
data_version: 1,
data: &buf,
};