Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.

Already on GitHub? Sign in to your account

serde support for core types & VariantDispatch #743

Merged
merged 1 commit into from Sep 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should simply serialize as the enumerator's name, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It actually generates code for vist_str, visit_bytes, and visit_u64 and lets the Serializer/Deserializer implementations choose how to represent it via serialize_unit_variant/deserialize_identifier. The u64 representation is generated sequentially in source code order, ignoring any custom discriminants. So binary formats don't have to store unnecessary strings, but it generates more code than strictly necessary, and the serialized values don't line up with their values in code (which is of course only really relevant if you're trying to debug a binary format).
https://gist.github.com/Waridley/5fc1faa65260aef4b55a95a864ec767d

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the elaboration! 馃憤馃徏

Ugh, that expanded code reminds me why we have macros, but also why their compilation takes so long 馃槵

#[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};
This conversation was marked as resolved.
Show resolved Hide resolved
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] = &[
Bromeon marked this conversation as resolved.
Show resolved Hide resolved
$(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