187 lines
6.1 KiB
Rust
187 lines
6.1 KiB
Rust
use crate::errors::Error;
|
|
use crate::models::proto::cofd::cofd_sheet::*;
|
|
use crate::models::proto::cofd::*;
|
|
use crate::models::users::User;
|
|
use prost::bytes::BytesMut;
|
|
use serde_derive::Serialize;
|
|
use strum::{EnumIter, EnumString};
|
|
|
|
/// 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.
|
|
pub(crate) trait Visibility {
|
|
/// User ID that owns this character.
|
|
fn user_id(&self) -> i32;
|
|
|
|
/// If the character is publicly visible.
|
|
fn viewable(&self) -> bool;
|
|
|
|
/// Transform to an Option that holds the character, if the
|
|
/// character is viewable to a potentially existing user. A
|
|
/// character is "visible" if the public viewable property is set
|
|
/// to true, or the user is the owner of the character. Consumes
|
|
/// self.
|
|
fn as_visible_for(self, user: Option<&User>) -> Option<Self>
|
|
where
|
|
Self: std::marker::Sized,
|
|
{
|
|
if self.viewable() || user.map(|u| u.id) == Some(self.user_id()) {
|
|
Some(self)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Enum representing all game systems supported by the character
|
|
/// service. Game systems are kept unique instead of lumping them
|
|
/// together under common umbrella systems, even if the different
|
|
/// games use the same (or similar) character sheets. This is because
|
|
/// of the possibility for slight differences in rules and data
|
|
/// between even similar systems. It's simpler to err on the side of
|
|
/// uniqueness. Enum variants are also versioned, in case of drastic
|
|
/// rewrites or migrations in the future.
|
|
#[derive(Debug, Serialize, PartialEq, Clone, Copy, EnumIter, EnumString, sqlx::Type)]
|
|
#[sqlx(rename_all = "snake_case")]
|
|
pub enum CharacterDataType {
|
|
ChroniclesOfDarkness,
|
|
Changeling,
|
|
}
|
|
|
|
impl CharacterDataType {
|
|
/// Create the default serialized protobuf data (character sheet)
|
|
/// for the game system represented by the enum variant.
|
|
pub fn default_serialized_data(&self) -> Result<BytesMut, Error> {
|
|
use prost::Message;
|
|
use CharacterDataType::*;
|
|
let data: BytesMut = match self {
|
|
ChroniclesOfDarkness => {
|
|
let sheet = CofdSheet::default_sheet();
|
|
let mut buf = BytesMut::with_capacity(std::mem::size_of_val(&sheet));
|
|
sheet.encode(&mut buf)?;
|
|
buf
|
|
}
|
|
Changeling => {
|
|
let sheet = ChangelingSheet::default_sheet();
|
|
let mut buf = BytesMut::with_capacity(std::mem::size_of_val(&sheet));
|
|
sheet.encode(&mut buf)?;
|
|
buf
|
|
}
|
|
};
|
|
|
|
Ok(data)
|
|
}
|
|
}
|
|
|
|
/// An entry that appears in a user's character list. Properties are
|
|
/// in order of table columns.
|
|
#[derive(Serialize, Debug, sqlx::FromRow)]
|
|
pub struct Character {
|
|
pub id: i32,
|
|
pub user_id: i32,
|
|
pub viewable: bool,
|
|
pub character_name: String,
|
|
pub data_type: CharacterDataType,
|
|
pub data_version: i32,
|
|
pub data: Vec<u8>,
|
|
}
|
|
|
|
impl Visibility for Character {
|
|
fn user_id(&self) -> i32 {
|
|
self.user_id
|
|
}
|
|
|
|
fn viewable(&self) -> bool {
|
|
self.viewable
|
|
}
|
|
}
|
|
|
|
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 {
|
|
ChroniclesOfDarkness => Box::new(self.try_deserialize::<CofdSheet>()?),
|
|
Changeling => Box::new(self.try_deserialize::<ChangelingSheet>()?),
|
|
};
|
|
|
|
Ok(decoded)
|
|
}
|
|
|
|
/// Upgrade the stored protobuf character data to its newest
|
|
/// iteration, if new types have been created. Consumes self.
|
|
pub fn uprade(self) -> Result<Character, Error> {
|
|
// Currently, this just returns itself because there are no
|
|
// iterations. But we could for example go from CofdSheet v1
|
|
// to CofdSheet v2 by deserializing v1, applying a migration
|
|
// to v2, then reserializing, and copying over all other
|
|
// fields.
|
|
Ok(self)
|
|
}
|
|
|
|
/// Update the existing character with new serialized protobuf
|
|
/// data. Consumes the data.
|
|
pub fn update_data<T>(&mut self, data: T) -> Result<(), Error>
|
|
where
|
|
T: prost::Message + std::default::Default,
|
|
{
|
|
let mut buf = BytesMut::with_capacity(std::mem::size_of_val(&data));
|
|
data.encode(&mut buf)?;
|
|
self.data = buf.to_vec();
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Same as regular character type, but without the actual protobuf
|
|
/// data loaded into memory.
|
|
#[derive(Serialize, Debug, sqlx::FromRow)]
|
|
pub struct StrippedCharacter {
|
|
pub id: i32,
|
|
pub user_id: i32,
|
|
pub viewable: bool,
|
|
pub character_name: String,
|
|
pub data_type: CharacterDataType,
|
|
pub data_version: i32,
|
|
}
|
|
|
|
impl Visibility for StrippedCharacter {
|
|
fn user_id(&self) -> i32 {
|
|
self.user_id
|
|
}
|
|
|
|
fn viewable(&self) -> bool {
|
|
self.viewable
|
|
}
|
|
}
|
|
|
|
/// Represents insert of a new character into the database. Property
|
|
/// names correspond to columns.
|
|
pub struct NewCharacter<'a> {
|
|
pub user_id: i32,
|
|
pub viewable: bool,
|
|
pub character_name: &'a str,
|
|
pub data_type: CharacterDataType,
|
|
pub data_version: i32,
|
|
pub data: &'a [u8],
|
|
}
|