use crate::{Error, TypeTreeValue};
use serde::{de::{value::{MapDeserializer, SeqDeserializer}, IntoDeserializer}, forward_to_deserialize_any};
#[derive(Debug)]
pub struct Deserializer<'de>(&'de TypeTreeValue);
impl<'de> Deserializer<'de> {
pub fn new(input: &'de TypeTreeValue) -> Self {
Self(input)
}
}
impl<'de> serde::de::Deserializer<'de> for Deserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>
{
match self.0 {
TypeTreeValue::SInt8(v) => visitor.visit_i8(*v),
TypeTreeValue::UInt8(v) => visitor.visit_u8(*v),
TypeTreeValue::Char(v) => visitor.visit_char(*v),
TypeTreeValue::SInt16(v) => visitor.visit_i16(*v),
TypeTreeValue::UInt16(v) => visitor.visit_u16(*v),
TypeTreeValue::SInt32(v) => visitor.visit_i32(*v),
TypeTreeValue::UInt32(v) | TypeTreeValue::Type(v) => visitor.visit_u32(*v),
TypeTreeValue::SInt64(v) => visitor.visit_i64(*v),
TypeTreeValue::UInt64(v) | TypeTreeValue::FileSize(v) => visitor.visit_u64(*v),
TypeTreeValue::Float(v) => visitor.visit_f32(*v),
TypeTreeValue::Double(v) => visitor.visit_f64(*v),
TypeTreeValue::Bool(v) => visitor.visit_bool(*v),
TypeTreeValue::String(v) => visitor.visit_borrowed_str(v),
TypeTreeValue::TypelessData(v) => visitor.visit_bytes(v),
TypeTreeValue::Map(v) => visitor.visit_seq(SeqDeserializer::new(
v.iter().map(|&(ref key, ref value)| PairDeserializer([key, value]))
)),
TypeTreeValue::Array(v) => visitor.visit_seq(SeqDeserializer::new(v.iter())),
TypeTreeValue::Class(v) => visitor.visit_map(MapDeserializer::new(
v.iter().map(|(name, value)| (name.as_str(), value))
))
}
}
fn deserialize_option<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>
{
visitor.visit_some(self)
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf unit unit_struct seq tuple tuple_struct map
struct newtype_struct enum identifier ignored_any
}
}
#[derive(Debug)]
pub struct PairDeserializer<'de>([&'de TypeTreeValue; 2]);
impl<'de> serde::de::Deserializer<'de> for PairDeserializer<'de> {
type Error = Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>
{
visitor.visit_seq(SeqDeserializer::new(self.0.into_iter()))
}
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
}
impl<'de> IntoDeserializer<'de, Error> for PairDeserializer<'de> {
type Deserializer = PairDeserializer<'de>;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
#[cfg(all(test, feature = "objects"))]
mod tests {
use std::collections::HashMap;
use serde::Deserialize;
use super::super::*;
use crate::objects::classes::*;
fn class_value(fields: Vec<(&str, Value)>) -> Value {
Value::Class(fields.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
}
#[test]
fn aabb_deserialization() {
let value = class_value(vec![
("m_Center", Value::Class(HashMap::from([
("x".to_string(), Value::Float(1.0)),
("y".to_string(), Value::Float(2.0)),
("z".to_string(), Value::Float(3.0)),
]))),
("m_Extent", Value::Class(HashMap::from([
("x".to_string(), Value::Float(4.0)),
("y".to_string(), Value::Float(5.0)),
("z".to_string(), Value::Float(6.0)),
]))),
]);
let deserializer = Deserializer::new(&value);
let aabb = AABB::deserialize(deserializer).unwrap();
assert_eq!(aabb.m_Center.x, 1.0);
assert_eq!(aabb.m_Center.y, 2.0);
assert_eq!(aabb.m_Center.z, 3.0);
assert_eq!(aabb.m_Extent.x, 4.0);
assert_eq!(aabb.m_Extent.y, 5.0);
assert_eq!(aabb.m_Extent.z, 6.0);
}
#[test]
fn astc_importer_deserialization() {
let value = class_value(vec![
("m_AssetBundleName", Value::String("bundle".to_string())),
("m_AssetBundleVariant", Value::String("variant".to_string())),
("m_Name", Value::String("asset".to_string())),
("m_UserData", Value::String("data".to_string())),
]);
let deserializer = Deserializer::new(&value);
let astc = ASTCImporter::deserialize(deserializer).unwrap();
assert_eq!(astc.m_AssetBundleName, "bundle");
assert_eq!(astc.m_AssetBundleVariant, "variant");
assert_eq!(astc.m_Name, "asset");
assert_eq!(astc.m_UserData, "data");
}
#[test]
fn added_game_object_deserialization() {
let value = class_value(vec![
("addedObject", Value::Class(HashMap::from([
("m_FileID".to_string(), Value::SInt64(111)),
("m_PathID".to_string(), Value::SInt64(222)),
]))),
("insertIndex", Value::SInt32(2)),
("targetCorrespondingSourceObject", Value::Class(HashMap::from([
("m_FileID".to_string(), Value::SInt64(333)),
("m_PathID".to_string(), Value::SInt64(444)),
]))),
]);
let deserializer = Deserializer::new(&value);
let game_object = AddedGameObject::deserialize(deserializer).unwrap();
assert_eq!(game_object.addedObject.m_FileID, 111);
assert_eq!(game_object.addedObject.m_PathID, 222);
assert_eq!(game_object.insertIndex, 2);
assert_eq!(game_object.targetCorrespondingSourceObject.m_FileID, 333);
assert_eq!(game_object.targetCorrespondingSourceObject.m_PathID, 444);
}
#[test]
fn aim_constraint_deserialization() {
let value = class_value(vec![
("m_AffectRotationX", Value::Bool(true)),
("m_AffectRotationY", Value::Bool(false)),
("m_AffectRotationZ", Value::Bool(true)),
("m_AimVector", Value::Class(HashMap::from([
("x".to_string(), Value::Float(0.0)),
("y".to_string(), Value::Float(1.0)),
("z".to_string(), Value::Float(0.0)),
]))),
("m_Enabled", Value::UInt8(1)),
("m_GameObject", Value::Class(HashMap::from([
("m_FileID".to_string(), Value::SInt64(555)),
("m_PathID".to_string(), Value::SInt64(666)),
]))),
("m_RotationAtRest", Value::Class(HashMap::from([
("x".to_string(), Value::Float(0.0)),
("y".to_string(), Value::Float(0.0)),
("z".to_string(), Value::Float(0.0)),
]))),
("m_RotationOffset", Value::Class(HashMap::from([
("x".to_string(), Value::Float(0.1)),
("y".to_string(), Value::Float(0.2)),
("z".to_string(), Value::Float(0.3)),
]))),
("m_Sources", Value::Array(vec![])),
("m_UpType", Value::SInt32(0)),
("m_UpVector", Value::Class(HashMap::from([
("x".to_string(), Value::Float(0.0)),
("y".to_string(), Value::Float(1.0)),
("z".to_string(), Value::Float(0.0)),
]))),
("m_Weight", Value::Float(1.0)),
("m_WorldUpObject", Value::Class(HashMap::from([
("m_FileID".to_string(), Value::SInt64(777)),
("m_PathID".to_string(), Value::SInt64(888)),
]))),
("m_WorldUpVector", Value::Class(HashMap::from([
("x".to_string(), Value::Float(0.0)),
("y".to_string(), Value::Float(1.0)),
("z".to_string(), Value::Float(0.0)),
]))),
("m_Active", Value::Bool(true)),
("m_IsContraintActive", Value::Bool(false)),
]);
let deserializer = Deserializer::new(&value);
let aim = AimConstraint::deserialize(deserializer).unwrap();
assert_eq!(aim.m_AffectRotationX, true);
assert_eq!(aim.m_AffectRotationY, false);
assert_eq!(aim.m_AffectRotationZ, true);
assert_eq!(aim.m_AimVector.x, 0.0);
assert_eq!(aim.m_Enabled, 1);
assert_eq!(aim.m_GameObject.m_FileID, 555);
assert_eq!(aim.m_Weight, 1.0);
assert_eq!(aim.m_Active, Some(true));
assert_eq!(aim.m_IsContraintActive, Some(false));
}
#[test]
fn animation_clip_deserialization() {
let value = class_value(vec![
("m_Bounds", Value::Class(HashMap::from([
("m_Center".to_string(), Value::Class(HashMap::from([
("x".to_string(), Value::Float(0.0)),
("y".to_string(), Value::Float(0.0)),
("z".to_string(), Value::Float(0.0)),
]))),
("m_Extent".to_string(), Value::Class(HashMap::from([
("x".to_string(), Value::Float(1.0)),
("y".to_string(), Value::Float(1.0)),
("z".to_string(), Value::Float(1.0)),
]))),
]))),
("m_Compressed", Value::Bool(false)),
("m_CompressedRotationCurves", Value::Array(vec![])),
("m_Events", Value::Array(vec![])),
("m_FloatCurves", Value::Array(vec![])),
("m_Name", Value::String("clip".to_string())),
("m_PositionCurves", Value::Array(vec![])),
("m_RotationCurves", Value::Array(vec![])),
("m_SampleRate", Value::Float(60.0)),
("m_ScaleCurves", Value::Array(vec![])),
("m_WrapMode", Value::SInt32(0)),
("m_AnimationType", Value::SInt32(1)),
("m_HasGenericRootTransform", Value::Bool(true)),
("m_HasMotionFloatCurves", Value::Bool(false)),
("m_Legacy", Value::Bool(true)),
]);
let deserializer = Deserializer::new(&value);
let clip = AnimationClip::deserialize(deserializer).unwrap();
assert_eq!(clip.m_Bounds.m_Center.x, 0.0);
assert_eq!(clip.m_Compressed, false);
assert_eq!(clip.m_Name, "clip");
assert_eq!(clip.m_SampleRate, 60.0);
assert_eq!(clip.m_WrapMode, 0);
assert_eq!(clip.m_AnimationType, Some(1));
assert_eq!(clip.m_HasGenericRootTransform, Some(true));
assert_eq!(clip.m_HasMotionFloatCurves, Some(false));
assert_eq!(clip.m_Legacy, Some(true));
}
#[test]
fn human_template_deserialization() {
let value = class_value(vec![
("m_Name", Value::String("HumanModel".to_string())),
("m_BoneTemplate", Value::Map(vec![
(Value::String("a".to_string()), Value::String("b".to_string())),
(Value::String("c".to_string()), Value::String("d".to_string())),
])),
]);
let deserializer = Deserializer::new(&value);
let human_template = HumanTemplate::deserialize(deserializer).unwrap();
assert_eq!(human_template.m_Name, "HumanModel");
assert_eq!(
human_template.m_BoneTemplate,
vec![
("a".to_string(), "b".to_string()),
("c".to_string(), "d".to_string())
]
);
}
}