Skip to content

Commit

Permalink
Merge #743
Browse files Browse the repository at this point in the history
743: serde support for core types & VariantDispatch r=Bromeon a=Waridley

I got curious about how hard it would be to add serde support, and ended up basically fully implementing it so I figured I should start a PR 😂

There are no tests yet, and honestly I haven't really even tested it in my own project yet. I'll be doing so over the next few days probably, but for now I'll open this as a draft to allow for comments, feedback, and opinions.

The implementations for `Object` types is a bit hacky, but I really like the idea of being able to replace Godot's resource files with something more Rust-y. Since I had to wrap the references in newtypes, I suppose they could be part of a separate crate anyway, unless anyone has a better idea of how to get around the orphan rules. Even if we can implement serde's traits directly on `gdnative_core` types, there should probably be some discussion about whether it's actually desirable to skip properties that are set to their defaults (this would end up serializing a **LOT** of properties otherwise).

Would close #9 

Co-authored-by: Waridley <Waridley64@gmail.com>
  • Loading branch information
bors[bot] and Waridley committed Sep 23, 2021
2 parents 490ae0f + 58fa3b3 commit cb2bda2
Show file tree
Hide file tree
Showing 22 changed files with 822 additions and 2 deletions.
1 change: 1 addition & 0 deletions gdnative-core/Cargo.toml
Expand Up @@ -24,6 +24,7 @@ glam = "0.18.0"
indexmap = "1.7.0"
ahash = "0.7.4"
once_cell = "1.8.0"
serde = { version = "1", features = ["derive"], optional = true }

gdnative-impl-proc-macros = { path = "../impl/proc_macros", version = "=0.9.3" }

Expand Down
1 change: 1 addition & 0 deletions gdnative-core/src/core_types/color.rs
Expand Up @@ -6,6 +6,7 @@ use crate::core_types::GodotString;
/// RGBA color with 32 bits floating point components.
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Color {
pub r: f32,
pub g: f32,
Expand Down
1 change: 1 addition & 0 deletions gdnative-core/src/core_types/error.rs
Expand Up @@ -2,6 +2,7 @@ use crate::sys;

/// Error codes used in various Godot APIs.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(u32)]
pub enum GodotError {
Failed = sys::godot_error_GODOT_FAILED as u32,
Expand Down
1 change: 1 addition & 0 deletions gdnative-core/src/core_types/geom/aabb.rs
Expand Up @@ -3,6 +3,7 @@ use crate::core_types::Vector3;
/// Axis-aligned bounding box.
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Aabb {
pub position: Vector3,
pub size: Vector3,
Expand Down
1 change: 1 addition & 0 deletions gdnative-core/src/core_types/geom/basis.rs
Expand Up @@ -5,6 +5,7 @@ use glam::Mat3;
/// A 3x3 matrix.
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Basis {
pub elements: [Vector3; 3],
}
Expand Down
1 change: 1 addition & 0 deletions gdnative-core/src/core_types/geom/plane.rs
Expand Up @@ -3,6 +3,7 @@ use crate::core_types::{IsEqualApprox, Vector3};
/// Plane in hessian form.
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Plane {
pub normal: Vector3,
pub d: f32,
Expand Down
1 change: 1 addition & 0 deletions gdnative-core/src/core_types/geom/transform.rs
Expand Up @@ -3,6 +3,7 @@ use crate::core_types::{Basis, Vector3};
/// 3D Transformation (3x4 matrix) Using basis + origin representation.
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Transform {
/// The basis is a matrix containing 3 Vector3 as its columns: X axis, Y axis, and Z axis.
/// These vectors can be interpreted as the basis vectors of local coordinate system
Expand Down
2 changes: 1 addition & 1 deletion gdnative-core/src/core_types/mod.rs
Expand Up @@ -43,7 +43,7 @@ pub use rid::*;
pub use string::*;
pub use string_array::*;
pub use transform2d::*;
pub use typed_array::TypedArray;
pub use typed_array::{Element, TypedArray};
pub use variant::*;
pub use variant_array::*;
pub use vector2::*;
Expand Down
57 changes: 57 additions & 0 deletions gdnative-core/src/core_types/node_path.rs
Expand Up @@ -176,3 +176,60 @@ impl fmt::Debug for NodePath {
write!(f, "NodePath({})", self.to_string())
}
}

#[cfg(feature = "serde")]
mod serialize {
use super::*;
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt::Formatter;

impl Serialize for NodePath {
#[inline]
fn serialize<S>(&self, ser: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
ser.serialize_newtype_struct("NodePath", &*self.to_string())
}
}

impl<'de> Deserialize<'de> for NodePath {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct NodePathVisitor;

impl<'de> Visitor<'de> for NodePathVisitor {
type Value = NodePath;

fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a NodePath")
}

fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(NodePath::from_str(s))
}

fn visit_newtype_struct<D>(
self,
deserializer: D,
) -> Result<Self::Value, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_str(self)
}
}

deserializer.deserialize_newtype_struct("NodePath", NodePathVisitor)
}
}
}
1 change: 1 addition & 0 deletions gdnative-core/src/core_types/quat.rs
Expand Up @@ -3,6 +3,7 @@ use glam::EulerRot;
use std::ops::{Mul, Neg};

#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Quat {
pub x: f32,
Expand Down
2 changes: 2 additions & 0 deletions gdnative-core/src/core_types/rect2.rs
@@ -1,5 +1,7 @@
use super::Vector2;

#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Rect2 {
pub position: Vector2,
Expand Down
50 changes: 50 additions & 0 deletions gdnative-core/src/core_types/string.rs
Expand Up @@ -612,6 +612,56 @@ where
}
}

#[cfg(feature = "serde")]
mod serialize {
use super::*;
use serde::{
de::{Error, Visitor},
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt::Formatter;

impl Serialize for GodotString {
#[inline]
fn serialize<S>(
&self,
serializer: S,
) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
serializer.serialize_str(&*self.to_string())
}
}

#[cfg(feature = "serde")]
impl<'de> serialize::Deserialize<'de> for GodotString {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
struct GodotStringVisitor;
impl<'de> Visitor<'de> for GodotStringVisitor {
type Value = GodotString;

fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str("a GodotString")
}

fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: Error,
{
Ok(GodotString::from(s))
}
}

deserializer.deserialize_str(GodotStringVisitor)
}
}
}

godot_test!(test_string {
use crate::core_types::{GodotString, Variant, VariantType, ToVariant};

Expand Down
2 changes: 2 additions & 0 deletions gdnative-core/src/core_types/transform2d.rs
@@ -1,5 +1,7 @@
use super::Vector2;

#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[repr(C)]
pub struct Transform2D {
pub x: Vector2,
Expand Down
60 changes: 60 additions & 0 deletions gdnative-core/src/core_types/typed_array.rs
Expand Up @@ -483,3 +483,63 @@ macros::impl_typed_array_element! {
mod private {
pub trait Sealed {}
}

#[cfg(feature = "serde")]
mod serialize {
use super::*;
use serde::{
de::{SeqAccess, Visitor},
ser::SerializeSeq,
Deserialize, Deserializer, Serialize, Serializer,
};
use std::fmt::Formatter;
use std::marker::PhantomData;

impl<T: Serialize + Element> Serialize for TypedArray<T> {
#[inline]
fn serialize<S>(&self, ser: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
let read = self.read();
let mut ser = ser.serialize_seq(Some(read.len()))?;
for e in read.iter() {
ser.serialize_element(e)?
}
ser.end()
}
}

impl<'de, T: Deserialize<'de> + Element> Deserialize<'de> for TypedArray<T> {
#[inline]
fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
where
D: Deserializer<'de>,
{
struct TypedArrayVisitor<T>(PhantomData<T>);
impl<'de, T: Deserialize<'de> + Element> Visitor<'de> for TypedArrayVisitor<T> {
type Value = TypedArray<T>;

fn expecting(&self, formatter: &mut Formatter) -> fmt::Result {
formatter.write_str(std::any::type_name::<Self::Value>())
}

fn visit_seq<A>(
self,
mut seq: A,
) -> Result<Self::Value, <A as SeqAccess<'de>>::Error>
where
A: SeqAccess<'de>,
{
let mut vec = seq.size_hint().map_or_else(Vec::new, Vec::with_capacity);
while let Some(val) = seq.next_element::<T>()? {
vec.push(val);
}
Ok(Self::Value::from_vec(vec))
}
}

deserializer.deserialize_seq(TypedArrayVisitor::<T>(PhantomData))
}
}
}
32 changes: 32 additions & 0 deletions gdnative-core/src/core_types/variant.rs
Expand Up @@ -9,6 +9,9 @@ use crate::object::*;
use crate::private::{get_api, ManuallyManagedClassPlaceholder};
use crate::thread_access::*;

#[cfg(feature = "serde")]
mod serialize;

// TODO: implement Debug, PartialEq, etc.

/// A `Variant` can represent many of godot's core types.
Expand Down Expand Up @@ -109,6 +112,13 @@ macro_rules! decl_variant_type {
)*
}

impl VariantType {
/// The potential names of VariantTypes. Mostly used for serialization.
pub const NAMES: &'static [&'static str] = &[
$(stringify!($variant),)*
];
}

/// Rust enum associating each primitive variant type to its value.
///
/// For `Variant`s containing objects, the original `Variant` is returned unchanged, due to
Expand All @@ -132,6 +142,19 @@ macro_rules! decl_variant_type {
}
}
}

impl<'a> From<&'a VariantDispatch> for Variant {
#[inline]
fn from(v: &'a VariantDispatch) -> Self {
match v {
$($(VariantDispatch::$variant(v) => {
let v: &$inner = v;
v.to_variant()
})?)*
_ => Variant::new()
}
}
}
}
}

Expand Down Expand Up @@ -173,6 +196,15 @@ impl VariantType {
pub fn from_sys(v: sys::godot_variant_type) -> VariantType {
unsafe { transmute(v as u32) }
}

/// The `stringify!` representation of this variant. Mostly used for serialization.
#[inline]
pub const fn name(self) -> &'static str {
// NOTE: this assumes that the discriminants remain sequential, since any additions to the
// VariantType enum would require a breaking change anyway since it is not marked as non-exhaustive.
// See also the Deserialize implementation in the serde submodule.
Self::NAMES[self as usize]
}
}

#[repr(u32)]
Expand Down

0 comments on commit cb2bda2

Please sign in to comment.