diff --git a/Cargo.toml b/Cargo.toml index 15c69e10..a5d784a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -50,7 +50,7 @@ bytemuck = { version = "1.5", optional = true, default-features = false } mint = { version = "0.5.8", optional = true, default-features = false } num-traits = { version = "0.2.14", optional = true, default-features = false } rand = { version = "0.8", optional = true, default-features = false } -serde = { version = "1.0", optional = true, default-features = false, features = ["derive"] } +serde = { version = "1.0", optional = true, default-features = false } rkyv = { version = "0.7", optional = true } bytecheck = { version = "0.6", optional = true, default-features = false} diff --git a/src/euler.rs b/src/euler.rs index c97a9628..cd658bf3 100644 --- a/src/euler.rs +++ b/src/euler.rs @@ -17,7 +17,6 @@ use num_traits::Float; /// /// YXZ can be used for yaw (y-axis), pitch (x-axis), roll (z-axis). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum EulerRot { /// Intrinsic three-axis rotation ZYX ZYX, diff --git a/src/features/impl_serde.rs b/src/features/impl_serde.rs index c3a44e08..547240fa 100644 --- a/src/features/impl_serde.rs +++ b/src/features/impl_serde.rs @@ -1000,3 +1000,181 @@ mod u32 { impl_serde_vec_types!(u32, UVec2, UVec3, UVec4); } + +mod euler { + use crate::EulerRot; + + impl serde::Serialize for EulerRot { + fn serialize(&self, serializer: S) -> Result { + match *self { + EulerRot::ZYX => { + serde::Serializer::serialize_unit_variant(serializer, "EulerRot", 0u32, "ZYX") + } + EulerRot::ZXY => { + serde::Serializer::serialize_unit_variant(serializer, "EulerRot", 1u32, "ZXY") + } + EulerRot::YXZ => { + serde::Serializer::serialize_unit_variant(serializer, "EulerRot", 2u32, "YXZ") + } + EulerRot::YZX => { + serde::Serializer::serialize_unit_variant(serializer, "EulerRot", 3u32, "YZX") + } + EulerRot::XYZ => { + serde::Serializer::serialize_unit_variant(serializer, "EulerRot", 4u32, "XYZ") + } + EulerRot::XZY => { + serde::Serializer::serialize_unit_variant(serializer, "EulerRot", 5u32, "XZY") + } + } + } + } + + impl<'de> serde::Deserialize<'de> for EulerRot { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[allow(clippy::upper_case_acronyms)] + enum Field { + ZYX, + ZXY, + YXZ, + YZX, + XYZ, + XZY, + } + struct FieldVisitor; + + impl<'de> serde::de::Visitor<'de> for FieldVisitor { + type Value = Field; + fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + core::fmt::Formatter::write_str(formatter, "variant identifier") + } + fn visit_u64(self, value: u64) -> Result + where + E: serde::de::Error, + { + match value { + 0u64 => Ok(Field::ZYX), + 1u64 => Ok(Field::ZXY), + 2u64 => Ok(Field::YXZ), + 3u64 => Ok(Field::YZX), + 4u64 => Ok(Field::XYZ), + 5u64 => Ok(Field::XZY), + _ => Err(serde::de::Error::invalid_value( + serde::de::Unexpected::Unsigned(value), + &"variant index 0 <= i < 6", + )), + } + } + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + match value { + "ZYX" => Ok(Field::ZYX), + "ZXY" => Ok(Field::ZXY), + "YXZ" => Ok(Field::YXZ), + "YZX" => Ok(Field::YZX), + "XYZ" => Ok(Field::XYZ), + "XZY" => Ok(Field::XZY), + _ => Err(serde::de::Error::unknown_variant(value, VARIANTS)), + } + } + fn visit_bytes(self, value: &[u8]) -> Result + where + E: serde::de::Error, + { + match value { + b"ZYX" => Ok(Field::ZYX), + b"ZXY" => Ok(Field::ZXY), + b"YXZ" => Ok(Field::YXZ), + b"YZX" => Ok(Field::YZX), + b"XYZ" => Ok(Field::XYZ), + b"XZY" => Ok(Field::XZY), + _ => { + #[cfg(feature = "std")] + let value = &String::from_utf8_lossy(value); + #[cfg(not(feature = "std"))] + let value = + core::str::from_utf8(value).unwrap_or("\u{fffd}\u{fffd}\u{fffd}"); + Err(serde::de::Error::unknown_variant(value, VARIANTS)) + } + } + } + } + impl<'de> serde::Deserialize<'de> for Field { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + serde::Deserializer::deserialize_identifier(deserializer, FieldVisitor) + } + } + struct Visitor<'de> { + marker: core::marker::PhantomData, + lifetime: core::marker::PhantomData<&'de ()>, + } + impl<'de> serde::de::Visitor<'de> for Visitor<'de> { + type Value = EulerRot; + fn expecting( + &self, + __formatter: &mut core::fmt::Formatter<'_>, + ) -> core::fmt::Result { + core::fmt::Formatter::write_str(__formatter, "enum EulerRot") + } + fn visit_enum(self, data: A) -> Result + where + A: serde::de::EnumAccess<'de>, + { + match serde::de::EnumAccess::variant(data)? { + (Field::ZYX, variant) => { + serde::de::VariantAccess::unit_variant(variant)?; + Ok(EulerRot::ZYX) + } + (Field::ZXY, variant) => { + serde::de::VariantAccess::unit_variant(variant)?; + Ok(EulerRot::ZXY) + } + (Field::YXZ, variant) => { + serde::de::VariantAccess::unit_variant(variant)?; + Ok(EulerRot::YXZ) + } + (Field::YZX, variant) => { + serde::de::VariantAccess::unit_variant(variant)?; + Ok(EulerRot::YZX) + } + (Field::XYZ, variant) => { + serde::de::VariantAccess::unit_variant(variant)?; + Ok(EulerRot::XYZ) + } + (Field::XZY, variant) => { + serde::de::VariantAccess::unit_variant(variant)?; + Ok(EulerRot::XZY) + } + } + } + } + const VARIANTS: &[&str] = &["ZYX", "ZXY", "YXZ", "YZX", "XYZ", "XZY"]; + serde::Deserializer::deserialize_enum( + deserializer, + "EulerRot", + VARIANTS, + Visitor { + marker: core::marker::PhantomData::, + lifetime: core::marker::PhantomData, + }, + ) + } + } + + #[test] + fn test_euler_rot_serde() { + let a = EulerRot::XYZ; + let serialized = serde_json::to_string(&a).unwrap(); + assert_eq!("\"XYZ\"", serialized); + let deserialized = serde_json::from_str(&serialized).unwrap(); + assert_eq!(a, deserialized); + } +}