From 6311ba0f2f978e0a6896e8ba06fbedabaa19b02b Mon Sep 17 00:00:00 2001 From: Francis De Brabandere Date: Thu, 18 Apr 2024 20:39:39 +0200 Subject: [PATCH] more enums --- src/vpx/gameitem/flasher.rs | 2 +- src/vpx/gameitem/gate.rs | 19 ++- src/vpx/gameitem/hittarget.rs | 198 +++++++++++++++++++--- src/vpx/renderprobe.rs | 298 ++++++++++++++++++++++++++++------ 4 files changed, 440 insertions(+), 77 deletions(-) diff --git a/src/vpx/gameitem/flasher.rs b/src/vpx/gameitem/flasher.rs index 96dcce5..bcd19cd 100644 --- a/src/vpx/gameitem/flasher.rs +++ b/src/vpx/gameitem/flasher.rs @@ -516,7 +516,7 @@ mod tests { #[test] #[should_panic = "Error(\"Invalid ImageAlignment value foo, expecting \\\"world\\\" or \\\"wrap\\\"\", line: 0, column: 0)"] fn test_alignment_json_fail() { - let json: Value = serde_json::Value::from("foo"); + let json = serde_json::Value::from("foo"); let _: ImageAlignment = serde_json::from_value(json).unwrap(); } } diff --git a/src/vpx/gameitem/gate.rs b/src/vpx/gameitem/gate.rs index 0accd12..5e759f0 100644 --- a/src/vpx/gameitem/gate.rs +++ b/src/vpx/gameitem/gate.rs @@ -60,7 +60,7 @@ impl<'de> Deserialize<'de> for GateType { "WireRectangle" => Ok(GateType::WireRectangle), "Plate" => Ok(GateType::Plate), "LongPlate" => Ok(GateType::LongPlate), - _ => Err(serde::de::Error::custom(format!("Unknown GateType: {}", s))), + _ => Err(serde::de::Error::custom(format!("Unknown GateType: {}, expecting \"WireW\", \"WireRectangle\", \"Plate\" or \"LongPlate\"", s))), } } } @@ -412,6 +412,7 @@ mod tests { use super::*; use pretty_assertions::assert_eq; + use serde_json::Value; #[test] fn test_write_read() { @@ -450,4 +451,20 @@ mod tests { let gate_read = Gate::biff_read(&mut BiffReader::new(writer.get_data())); assert_eq!(gate, gate_read); } + + #[test] + fn test_gate_type_json() { + let gate_type = GateType::WireRectangle; + let json = serde_json::to_string(&gate_type).unwrap(); + assert_eq!(json, "\"WireRectangle\""); + let gate_type_read: GateType = serde_json::from_str(&json).unwrap(); + assert_eq!(gate_type, gate_type_read); + } + + #[test] + #[should_panic = "Error(\"Unknown GateType: Unknown, expecting \\\"WireW\\\", \\\"WireRectangle\\\", \\\"Plate\\\" or \\\"LongPlate\\\"\", line: 0, column: 0)"] + fn test_gate_type_json_panic() { + let json = Value::from("Unknown"); + let _: GateType = serde_json::from_value(json).unwrap(); + } } diff --git a/src/vpx/gameitem/hittarget.rs b/src/vpx/gameitem/hittarget.rs index cfd3edf..0c81ebb 100644 --- a/src/vpx/gameitem/hittarget.rs +++ b/src/vpx/gameitem/hittarget.rs @@ -4,13 +4,154 @@ use serde::{Deserialize, Serialize}; use super::vertex3d::Vertex3D; +#[derive(Debug, PartialEq, Clone, Dummy)] +pub enum TargetType { + DropTargetBeveled = 1, + DropTargetSimple = 2, + HitTargetRound = 3, + HitTargetRectangle = 4, + HitFatTargetRectangle = 5, + HitFatTargetSquare = 6, + DropTargetFlatSimple = 7, + HitFatTargetSlim = 8, + HitTargetSlim = 9, +} + +impl From for TargetType { + fn from(value: u32) -> Self { + match value { + 1 => TargetType::DropTargetBeveled, + 2 => TargetType::DropTargetSimple, + 3 => TargetType::HitTargetRound, + 4 => TargetType::HitTargetRectangle, + 5 => TargetType::HitFatTargetRectangle, + 6 => TargetType::HitFatTargetSquare, + 7 => TargetType::DropTargetFlatSimple, + 8 => TargetType::HitFatTargetSlim, + 9 => TargetType::HitTargetSlim, + _ => panic!("Invalid TargetType value {}", value), + } + } +} + +impl From<&TargetType> for u32 { + fn from(value: &TargetType) -> Self { + match value { + TargetType::DropTargetBeveled => 1, + TargetType::DropTargetSimple => 2, + TargetType::HitTargetRound => 3, + TargetType::HitTargetRectangle => 4, + TargetType::HitFatTargetRectangle => 5, + TargetType::HitFatTargetSquare => 6, + TargetType::DropTargetFlatSimple => 7, + TargetType::HitFatTargetSlim => 8, + TargetType::HitTargetSlim => 9, + } + } +} + +/// Serialize TargetType as lowercase string +impl Serialize for TargetType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + match self { + TargetType::DropTargetBeveled => serializer.serialize_str("drop_target_beveled"), + TargetType::DropTargetSimple => serializer.serialize_str("drop_target_simple"), + TargetType::HitTargetRound => serializer.serialize_str("hit_target_round"), + TargetType::HitTargetRectangle => serializer.serialize_str("hit_target_rectangle"), + TargetType::HitFatTargetRectangle => { + serializer.serialize_str("hit_fat_target_rectangle") + } + TargetType::HitFatTargetSquare => serializer.serialize_str("hit_fat_target_square"), + TargetType::DropTargetFlatSimple => serializer.serialize_str("drop_target_flat_simple"), + TargetType::HitFatTargetSlim => serializer.serialize_str("hit_fat_target_slim"), + TargetType::HitTargetSlim => serializer.serialize_str("hit_target_slim"), + } + } +} + +/// Deserialize TargetType from lowercase string +/// or number for backwards compatibility +impl<'de> Deserialize<'de> for TargetType { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct TargetTypeVisitor; + + impl<'de> serde::de::Visitor<'de> for TargetTypeVisitor { + type Value = TargetType; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string or number representing a TargetType") + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + match value { + 1 => Ok(TargetType::DropTargetBeveled), + 2 => Ok(TargetType::DropTargetSimple), + 3 => Ok(TargetType::HitTargetRound), + 4 => Ok(TargetType::HitTargetRectangle), + 5 => Ok(TargetType::HitFatTargetRectangle), + 6 => Ok(TargetType::HitFatTargetSquare), + 7 => Ok(TargetType::DropTargetFlatSimple), + 8 => Ok(TargetType::HitFatTargetSlim), + 9 => Ok(TargetType::HitTargetSlim), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(value), + &"a number between 1 and 9", + )), + } + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "drop_target_beveled" => Ok(TargetType::DropTargetBeveled), + "drop_target_simple" => Ok(TargetType::DropTargetSimple), + "hit_target_round" => Ok(TargetType::HitTargetRound), + "hit_target_rectangle" => Ok(TargetType::HitTargetRectangle), + "hit_fat_target_rectangle" => Ok(TargetType::HitFatTargetRectangle), + "hit_fat_target_square" => Ok(TargetType::HitFatTargetSquare), + "drop_target_flat_simple" => Ok(TargetType::DropTargetFlatSimple), + "hit_fat_target_slim" => Ok(TargetType::HitFatTargetSlim), + "hit_target_slim" => Ok(TargetType::HitTargetSlim), + _ => Err(serde::de::Error::unknown_variant( + value, + &[ + "drop_target_beveled", + "drop_target_simple", + "hit_target_round", + "hit_target_rectangle", + "hit_fat_target_rectangle", + "hit_fat_target_square", + "drop_target_flat_simple", + "hit_fat_target_slim", + "hit_target_slim", + ], + )), + } + } + } + + deserializer.deserialize_any(TargetTypeVisitor) + } +} + #[derive(Debug, PartialEq, Dummy)] pub struct HitTarget { pub position: Vertex3D, pub size: Vertex3D, pub rot_z: f32, pub image: String, - pub target_type: i32, + pub target_type: TargetType, pub name: String, pub material: String, pub is_visible: bool, @@ -51,7 +192,7 @@ impl Default for HitTarget { let size = Vertex3D::new(32.0, 32.0, 32.0); let rot_z: f32 = 0.0; let image: String = Default::default(); - let target_type: i32 = HitTarget::TARGET_TYPE_DROP_TARGET_SIMPLE; + let target_type: TargetType = TargetType::DropTargetSimple; let name: String = Default::default(); let material: String = Default::default(); let is_visible: bool = true; @@ -124,7 +265,7 @@ struct HitTargetJson { size: Vertex3D, rot_z: f32, image: String, - target_type: i32, + target_type: TargetType, name: String, material: String, is_visible: bool, @@ -157,7 +298,7 @@ impl HitTargetJson { size: hit_target.size, rot_z: hit_target.rot_z, image: hit_target.image.clone(), - target_type: hit_target.target_type, + target_type: hit_target.target_type.clone(), name: hit_target.name.clone(), material: hit_target.material.clone(), is_visible: hit_target.is_visible, @@ -190,7 +331,7 @@ impl HitTargetJson { size: self.size, rot_z: self.rot_z, image: self.image.clone(), - target_type: self.target_type, + target_type: self.target_type.clone(), name: self.name.clone(), material: self.material.clone(), is_visible: self.is_visible, @@ -245,25 +386,13 @@ impl<'de> Deserialize<'de> for HitTarget { } } -impl HitTarget { - pub const TARGET_TYPE_DROP_TARGET_BEVELED: i32 = 1; - pub const TARGET_TYPE_DROP_TARGET_SIMPLE: i32 = 2; - pub const TARGET_TYPE_HIT_TARGET_ROUND: i32 = 3; - pub const TARGET_TYPE_HIT_TARGET_RECTANGLE: i32 = 4; - pub const TARGET_TYPE_HIT_FAT_TARGET_RECTANGLE: i32 = 5; - pub const TARGET_TYPE_HIT_FAT_TARGET_SQUARE: i32 = 6; - pub const TARGET_TYPE_DROP_TARGET_FLAT_SIMPLE: i32 = 7; - pub const TARGET_TYPE_HIT_FAT_TARGET_SLIM: i32 = 8; - pub const TARGET_TYPE_HIT_TARGET_SLIM: i32 = 9; -} - impl BiffRead for HitTarget { fn biff_read(reader: &mut BiffReader<'_>) -> Self { let mut position: Vertex3D = Default::default(); let mut size = Vertex3D::new(32.0, 32.0, 32.0); let mut rot_z: f32 = 0.0; let mut image: String = Default::default(); - let mut target_type: i32 = HitTarget::TARGET_TYPE_DROP_TARGET_SIMPLE; + let mut target_type: TargetType = TargetType::DropTargetSimple; let mut name: String = Default::default(); let mut material: String = Default::default(); let mut is_visible: bool = true; @@ -315,7 +444,7 @@ impl BiffRead for HitTarget { image = reader.get_string(); } "TRTY" => { - target_type = reader.get_i32(); + target_type = reader.get_u32().into(); } "NAME" => { name = reader.get_wide_string(); @@ -445,7 +574,7 @@ impl BiffWrite for HitTarget { writer.write_tagged("VSIZ", &self.size); writer.write_tagged_f32("ROTZ", self.rot_z); writer.write_tagged_string("IMAG", &self.image); - writer.write_tagged_i32("TRTY", self.target_type); + writer.write_tagged_u32("TRTY", (&self.target_type).into()); writer.write_tagged_wide_string("NAME", &self.name); writer.write_tagged_string("MATR", &self.material); writer.write_tagged_bool("TVIS", self.is_visible); @@ -498,6 +627,7 @@ impl BiffWrite for HitTarget { #[cfg(test)] mod tests { use crate::vpx::biff::BiffWriter; + use fake::{Fake, Faker}; use super::*; use pretty_assertions::assert_eq; @@ -512,7 +642,7 @@ mod tests { size: Vertex3D::new(rng.gen(), rng.gen(), rng.gen()), rot_z: rng.gen(), image: "test image".to_string(), - target_type: rng.gen(), + target_type: Faker.fake(), name: "test name".to_string(), material: "test material".to_string(), is_visible: rng.gen(), @@ -546,4 +676,30 @@ mod tests { let hittarget_read = HitTarget::biff_read(&mut BiffReader::new(writer.get_data())); assert_eq!(hittarget, hittarget_read); } + + #[test] + fn test_target_type_json() { + let sizing_type = TargetType::HitFatTargetRectangle; + let json = serde_json::to_string(&sizing_type).unwrap(); + assert_eq!(json, "\"hit_fat_target_rectangle\""); + let sizing_type_read: TargetType = serde_json::from_str(&json).unwrap(); + assert_eq!(sizing_type, sizing_type_read); + let json = serde_json::Value::from(1); + let sizing_type_read: TargetType = serde_json::from_value(json).unwrap(); + assert_eq!(TargetType::DropTargetBeveled, sizing_type_read); + } + + #[test] + #[should_panic = "Error(\"unknown variant `foo`, expected one of `drop_target_beveled`, `drop_target_simple`, `hit_target_round`, `hit_target_rectangle`, `hit_fat_target_rectangle`, `hit_fat_target_square`, `drop_target_flat_simple`, `hit_fat_target_slim`, `hit_target_slim`\", line: 0, column: 0)"] + fn test_target_type_json_fail_string() { + let json = serde_json::Value::from("foo"); + let _: TargetType = serde_json::from_value(json).unwrap(); + } + + #[test] + #[should_panic = "Error(\"invalid value: integer `0`, expected a number between 1 and 9\", line: 0, column: 0)"] + fn test_target_type_json_fail_number() { + let json = serde_json::Value::from(0); + let _: TargetType = serde_json::from_value(json).unwrap(); + } } diff --git a/src/vpx/renderprobe.rs b/src/vpx/renderprobe.rs index 4d0b440..553433f 100644 --- a/src/vpx/renderprobe.rs +++ b/src/vpx/renderprobe.rs @@ -9,6 +9,86 @@ enum RenderProbeType { ScreenSpaceTransparency = 1, } +impl From for RenderProbeType { + fn from(i: u32) -> Self { + match i { + 0 => RenderProbeType::PlaneReflection, + 1 => RenderProbeType::ScreenSpaceTransparency, + _ => panic!("Unknown MaterialType {}", i), + } + } +} + +impl From<&RenderProbeType> for u32 { + fn from(r: &RenderProbeType) -> Self { + match r { + RenderProbeType::PlaneReflection => 0, + RenderProbeType::ScreenSpaceTransparency => 1, + } + } +} + +/// Serialize as lowercase string +impl<'de> Deserialize<'de> for RenderProbeType { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + struct RenderProbeTypeVisitor; + impl<'de> serde::de::Visitor<'de> for RenderProbeTypeVisitor { + type Value = RenderProbeType; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string or a number") + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + match value { + 0 => Ok(RenderProbeType::PlaneReflection), + 1 => Ok(RenderProbeType::ScreenSpaceTransparency), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(value), + &"a number between 0 and 1", + )), + } + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "plane_reflection" => Ok(RenderProbeType::PlaneReflection), + "screen_space_transparency" => Ok(RenderProbeType::ScreenSpaceTransparency), + _ => Err(serde::de::Error::unknown_variant( + value, + &["plane_reflection", "screen_space_transparency"], + )), + } + } + } + deserializer.deserialize_any(RenderProbeTypeVisitor) + } +} + +/// Deserialize from lowercase string +/// or number for backwards compatibility +impl Serialize for RenderProbeType { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let s = match self { + RenderProbeType::PlaneReflection => "plane_reflection", + RenderProbeType::ScreenSpaceTransparency => "screen_space_transparency", + }; + serializer.serialize_str(s) + } +} + #[derive(Debug, Clone, PartialEq, Dummy)] enum ReflectionMode { /// No reflections @@ -27,6 +107,119 @@ enum ReflectionMode { Unknown = 6, } +impl From for ReflectionMode { + fn from(i: u32) -> Self { + match i { + 0 => ReflectionMode::None, + 1 => ReflectionMode::Balls, + 2 => ReflectionMode::Static, + 3 => ReflectionMode::StaticNBalls, + 4 => ReflectionMode::StaticNDynamic, + 5 => ReflectionMode::Dynamic, + 6 => ReflectionMode::Unknown, + _ => panic!("Unknown ReflectionMode {}", i), + } + } +} + +impl From<&ReflectionMode> for u32 { + fn from(r: &ReflectionMode) -> Self { + match r { + ReflectionMode::None => 0, + ReflectionMode::Balls => 1, + ReflectionMode::Static => 2, + ReflectionMode::StaticNBalls => 3, + ReflectionMode::StaticNDynamic => 4, + ReflectionMode::Dynamic => 5, + ReflectionMode::Unknown => 6, + } + } +} + +/// Serialize as lowercase string +impl Serialize for ReflectionMode { + fn serialize(&self, serializer: S) -> Result + where + S: serde::ser::Serializer, + { + let s = match self { + ReflectionMode::None => "none", + ReflectionMode::Balls => "balls", + ReflectionMode::Static => "static", + ReflectionMode::StaticNBalls => "static_and_balls", + ReflectionMode::StaticNDynamic => "static_and_dynamic", + ReflectionMode::Dynamic => "dynamic", + ReflectionMode::Unknown => "unknown", + }; + serializer.serialize_str(s) + } +} + +/// Deserialize from lowercase string +/// or number for backwards compatibility +impl<'de> Deserialize<'de> for ReflectionMode { + fn deserialize(deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + struct ReflectionModeVisitor; + impl<'de> serde::de::Visitor<'de> for ReflectionModeVisitor { + type Value = ReflectionMode; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("a string or a number") + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "none" => Ok(ReflectionMode::None), + "balls" => Ok(ReflectionMode::Balls), + "static" => Ok(ReflectionMode::Static), + "static_and_balls" => Ok(ReflectionMode::StaticNBalls), + "static_and_dynamic" => Ok(ReflectionMode::StaticNDynamic), + "dynamic" => Ok(ReflectionMode::Dynamic), + "unknown" => Ok(ReflectionMode::Unknown), + _ => Err(serde::de::Error::unknown_variant( + value, + &[ + "none", + "balls", + "static", + "static_and_balls", + "static_and_dynamic", + "dynamic", + "unknown", + ], + )), + } + } + + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + match value { + 0 => Ok(ReflectionMode::None), + 1 => Ok(ReflectionMode::Balls), + 2 => Ok(ReflectionMode::Static), + 3 => Ok(ReflectionMode::StaticNBalls), + 4 => Ok(ReflectionMode::StaticNDynamic), + 5 => Ok(ReflectionMode::Dynamic), + 6 => Ok(ReflectionMode::Unknown), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(value), + &"a number between 0 and 6", + )), + } + } + } + deserializer.deserialize_any(ReflectionModeVisitor) + } +} + #[derive(Debug, Clone, PartialEq, Dummy)] pub struct RenderProbe { type_: RenderProbeType, @@ -55,13 +248,13 @@ pub struct RenderProbeWithGarbage { #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub(crate) struct RenderProbeJson { - type_: i32, + type_: RenderProbeType, name: String, roughness: u32, #[serde(skip_serializing_if = "Option::is_none")] roughness_clear: Option, reflection_plane: Vertex4D, - reflection_mode: i32, + reflection_mode: ReflectionMode, #[serde(skip_serializing_if = "Option::is_none")] disable_light_reflection: Option, #[serde(skip_serializing_if = "Option::is_none")] @@ -77,12 +270,12 @@ impl RenderProbeJson { Some(render_probe_with_garbage.trailing_data.clone()) }; Self { - type_: render_probe.type_.to_i32(), + type_: render_probe.type_.clone(), name: render_probe.name.clone(), roughness: render_probe.roughness, roughness_clear: render_probe.roughness_clear, reflection_plane: render_probe.reflection_plane, - reflection_mode: render_probe.reflection_mode.to_i32(), + reflection_mode: render_probe.reflection_mode.clone(), disable_light_reflection: render_probe.disable_light_reflection, trailing_data, } @@ -90,12 +283,12 @@ impl RenderProbeJson { pub fn to_renderprobe(&self) -> RenderProbeWithGarbage { let renderprobe = RenderProbe { - type_: RenderProbeType::from_i32(self.type_), + type_: self.type_.clone(), name: self.name.clone(), roughness: self.roughness, roughness_clear: self.roughness_clear, reflection_plane: self.reflection_plane, - reflection_mode: ReflectionMode::from_i32(self.reflection_mode), + reflection_mode: self.reflection_mode.clone(), disable_light_reflection: self.disable_light_reflection, }; RenderProbeWithGarbage { @@ -105,48 +298,6 @@ impl RenderProbeJson { } } -impl RenderProbeType { - fn from_i32(i: i32) -> Self { - match i { - 0 => RenderProbeType::PlaneReflection, - 1 => RenderProbeType::ScreenSpaceTransparency, - _ => panic!("Unknown MaterialType {}", i), - } - } - fn to_i32(&self) -> i32 { - match self { - RenderProbeType::PlaneReflection => 0, - RenderProbeType::ScreenSpaceTransparency => 1, - } - } -} - -impl ReflectionMode { - fn from_i32(i: i32) -> Self { - match i { - 0 => ReflectionMode::None, - 1 => ReflectionMode::Balls, - 2 => ReflectionMode::Static, - 3 => ReflectionMode::StaticNBalls, - 4 => ReflectionMode::StaticNDynamic, - 5 => ReflectionMode::Dynamic, - 6 => ReflectionMode::Unknown, - _ => panic!("Unknown ReflectionMode {}", i), - } - } - fn to_i32(&self) -> i32 { - match self { - ReflectionMode::None => 0, - ReflectionMode::Balls => 1, - ReflectionMode::Static => 2, - ReflectionMode::StaticNBalls => 3, - ReflectionMode::StaticNDynamic => 4, - ReflectionMode::Dynamic => 5, - ReflectionMode::Unknown => 6, - } - } -} - impl Default for RenderProbe { fn default() -> Self { RenderProbe { @@ -172,12 +323,12 @@ impl BiffRead for RenderProbe { let tag = reader.tag(); let tag_str = tag.as_str(); match tag_str { - "TYPE" => render_probe.type_ = RenderProbeType::from_i32(reader.get_i32()), + "TYPE" => render_probe.type_ = reader.get_u32().into(), "NAME" => render_probe.name = reader.get_string(), "RBAS" => render_probe.roughness = reader.get_u32(), "RCLE" => render_probe.roughness_clear = Some(reader.get_u32()), "RPLA" => render_probe.reflection_plane = Vertex4D::biff_read(reader), - "RMOD" => render_probe.reflection_mode = ReflectionMode::from_i32(reader.get_i32()), + "RMOD" => render_probe.reflection_mode = reader.get_u32().into(), "RLMP" => render_probe.disable_light_reflection = Some(reader.get_bool()), _ => { println!( @@ -195,14 +346,14 @@ impl BiffRead for RenderProbe { impl BiffWrite for RenderProbe { fn biff_write(&self, writer: &mut BiffWriter) { - writer.write_tagged_i32("TYPE", self.type_.to_i32()); + writer.write_tagged_u32("TYPE", (&self.type_).into()); writer.write_tagged_string("NAME", &self.name); writer.write_tagged_u32("RBAS", self.roughness); if let Some(rcle) = self.roughness_clear { writer.write_tagged_u32("RCLE", rcle); } writer.write_tagged("RPLA", &self.reflection_plane); - writer.write_tagged_i32("RMOD", self.reflection_mode.to_i32()); + writer.write_tagged_u32("RMOD", (&self.reflection_mode).into()); if let Some(disable_light_reflection) = self.disable_light_reflection { writer.write_tagged_bool("RLMP", disable_light_reflection); } @@ -235,17 +386,18 @@ impl BiffWrite for RenderProbeWithGarbage { mod tests { use super::*; use crate::vpx::biff::BiffReader; + use fake::{Fake, Faker}; use pretty_assertions::assert_eq; #[test] fn test_write_read() { let render_probe = RenderProbe { - type_: RenderProbeType::ScreenSpaceTransparency, + type_: Faker.fake(), name: "test".to_string(), roughness: 1, roughness_clear: Some(2), reflection_plane: Vertex4D::new(1.0, 2.0, 3.0, 4.0), - reflection_mode: ReflectionMode::Dynamic, + reflection_mode: Faker.fake(), disable_light_reflection: Some(false), }; let mut writer = BiffWriter::new(); @@ -300,4 +452,42 @@ mod tests { RenderProbeJson::to_renderprobe(&serde_json::from_str(&json).unwrap()); assert_eq!(render_probe_with_garbage, render_probe_with_garbage_read); } + + #[test] + fn test_reflection_mode_json() { + let sizing_type = ReflectionMode::Balls; + let json = serde_json::to_string(&sizing_type).unwrap(); + assert_eq!(json, "\"balls\""); + let sizing_type_read: ReflectionMode = serde_json::from_str(&json).unwrap(); + assert_eq!(sizing_type, sizing_type_read); + let json = serde_json::Value::from(3); + let sizing_type_read: ReflectionMode = serde_json::from_value(json).unwrap(); + assert_eq!(ReflectionMode::StaticNBalls, sizing_type_read); + } + + #[test] + #[should_panic = "Error(\"unknown variant `foo`, expected one of `none`, `balls`, `static`, `static_and_balls`, `static_and_dynamic`, `dynamic`, `unknown`\", line: 0, column: 0)"] + fn test_reflection_mode_json_fail_string() { + let json = serde_json::Value::from("foo"); + let _: ReflectionMode = serde_json::from_value(json).unwrap(); + } + + #[test] + fn test_render_probe_type_json() { + let sizing_type = RenderProbeType::ScreenSpaceTransparency; + let json = serde_json::to_string(&sizing_type).unwrap(); + assert_eq!(json, "\"screen_space_transparency\""); + let sizing_type_read: RenderProbeType = serde_json::from_str(&json).unwrap(); + assert_eq!(sizing_type, sizing_type_read); + let json = serde_json::Value::from(0); + let sizing_type_read: RenderProbeType = serde_json::from_value(json).unwrap(); + assert_eq!(RenderProbeType::PlaneReflection, sizing_type_read); + } + + #[test] + #[should_panic = "Error(\"unknown variant `foo`, expected `plane_reflection` or `screen_space_transparency`\", line: 0, column: 0)"] + fn test_render_probe_type_json_fail_string() { + let json = serde_json::Value::from("foo"); + let _: RenderProbeType = serde_json::from_value(json).unwrap(); + } }