Skip to content

Commit

Permalink
Merge #246
Browse files Browse the repository at this point in the history
246: Derive From Variant To Variant r=Bromeon a=astrale-sharp

Closes #245 

Co-authored-by: Astrale <jules180798@yahoo.fr>
  • Loading branch information
bors[bot] and Astrale authored May 24, 2023
2 parents 3b30209 + cef7baa commit 34f0ffe
Show file tree
Hide file tree
Showing 14 changed files with 657 additions and 19 deletions.
2 changes: 1 addition & 1 deletion godot-core/src/builtin/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ impl<T: VariantMetadata> ToVariant for Array<T> {
impl<T: VariantMetadata> FromVariant for Array<T> {
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
if variant.get_type() != Self::variant_type() {
return Err(VariantConversionError);
return Err(VariantConversionError::BadType);
}

let array = unsafe {
Expand Down
6 changes: 3 additions & 3 deletions godot-core/src/builtin/variant/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ macro_rules! impl_variant_traits {
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
// Type check -- at the moment, a strict match is required.
if variant.get_type() != Self::variant_type() {
return Err(VariantConversionError)
return Err(VariantConversionError::BadType)
}

// In contrast to T -> Variant, the conversion Variant -> T assumes
Expand Down Expand Up @@ -92,7 +92,7 @@ macro_rules! impl_variant_traits_int {
impl FromVariant for $T {
fn try_from_variant(v: &Variant) -> Result<Self, VariantConversionError> {
i64::try_from_variant(v)
.and_then(|i| <$T>::try_from(i).map_err(|_e| VariantConversionError))
.and_then(|i| <$T>::try_from(i).map_err(|_e| VariantConversionError::BadType))
}
}

Expand Down Expand Up @@ -251,7 +251,7 @@ impl<T: EngineEnum> ToVariant for T {
impl<T: EngineEnum> FromVariant for T {
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
<i32 as FromVariant>::try_from_variant(variant)
.and_then(|int| Self::try_from_ord(int).ok_or(VariantConversionError))
.and_then(|int| Self::try_from_ord(int).ok_or(VariantConversionError::BadType))
}
}

Expand Down
9 changes: 6 additions & 3 deletions godot-core/src/builtin/variant/variant_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,14 @@ pub trait ToVariant {
// ----------------------------------------------------------------------------------------------------------------------------------------------

#[derive(Eq, PartialEq, Debug)]
pub struct VariantConversionError;
/*pub enum VariantConversionError {
//pub struct VariantConversionError;
pub enum VariantConversionError {
/// Variant type does not match expected type
BadType,

/// Variant value cannot be represented in target type
BadValue,
}*/

/// Variant value is missing a value for the target type
MissingValue,
}
2 changes: 1 addition & 1 deletion godot-core/src/obj/gd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ impl<T: GodotClass> FromVariant for Gd<T> {
// TODO(#234) remove this cast when Godot stops allowing illegal conversions
// (See https://github.com/godot-rust/gdext/issues/158)
.and_then(|obj| obj.owned_cast().ok())
.ok_or(VariantConversionError)
.ok_or(VariantConversionError::BadType)
}
}

Expand Down
2 changes: 1 addition & 1 deletion godot-core/src/obj/instance_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ unsafe impl GodotFfi for InstanceId {
impl FromVariant for InstanceId {
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
i64::try_from_variant(variant)
.and_then(|i| InstanceId::try_from_i64(i).ok_or(VariantConversionError))
.and_then(|i| InstanceId::try_from_i64(i).ok_or(VariantConversionError::BadValue))
}
}

Expand Down
200 changes: 200 additions & 0 deletions godot-macros/src/derive_from_variant.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/

use crate::util::{decl_get_info, DeclInfo};
use crate::ParseResult;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use venial::{Declaration, StructFields};

pub fn transform(decl: Declaration) -> ParseResult<TokenStream> {
let DeclInfo {
where_,
generic_params,
name,
name_string,
} = decl_get_info(&decl);
let mut body = quote! {
let root = variant.try_to::<godot::builtin::Dictionary>()?;
let root = root.get(#name_string).ok_or(godot::builtin::VariantConversionError::BadType)?;
};

match decl {
Declaration::Struct(s) => match s.fields {
venial::StructFields::Unit => {
body = quote! {
#body
return Ok(Self);
}
}
venial::StructFields::Tuple(fields) => {
if fields.fields.len() == 1 {
body = quote! {
#body
let root = root.try_to()?;
Ok(Self(root))
};
} else {
let ident_and_set = fields.fields.iter().enumerate().map(|(k, _)| {
let ident = format_ident!("__{}", k);
(
ident.clone(),
quote! {
let #ident = root.pop_front().ok_or(godot::builtin::VariantConversionError::MissingValue)?;
},

)
});
let (idents, ident_set): (Vec<_>, Vec<_>) = ident_and_set.unzip();
body = quote! {
#body
let mut root = root.try_to::<godot::builtin::Array<godot::builtin::Variant>>()?;
#(
#ident_set

)*
Ok(Self(
#(#idents.try_to()?,)*
))
};
}
}
venial::StructFields::Named(fields) => {
let fields = fields.fields.iter().map(|(field, _)|{
let ident = &field.name;
let string_ident = &field.name.to_string();
(
quote!{
let #ident = root.get(#string_ident).ok_or(godot::builtin::VariantConversionError::MissingValue)?;
},

quote!{
#ident :#ident.try_to()?
}
)

});
let (set_idents, set_self): (Vec<_>, Vec<_>) = fields.unzip();
body = quote! {
#body
let root = root.try_to::<godot::builtin::Dictionary>()?;
#(
#set_idents
)*
Ok(Self{ #(#set_self,)* })
}
}
},
Declaration::Enum(enum_) => {
if enum_.variants.is_empty() {
body = quote! {
panic!();
}
} else {
let mut matches = quote! {};
for (enum_v, _) in &enum_.variants.inner {
let variant_name = enum_v.name.clone();
let variant_name_string = enum_v.name.to_string();
let if_let_content = match &enum_v.contents {
StructFields::Unit => quote! {
let child = root.try_to::<String>();
if child == Ok(String::from(#variant_name_string)) {
return Ok(Self::#variant_name);
}
},
StructFields::Tuple(fields) => {
if fields.fields.len() == 1 {
let (field, _) = fields.fields.first().unwrap();
let field_type = &field.ty;
quote! {
let child = root.try_to::<godot::builtin::Dictionary>();
if let Ok(child) = child {
if let Some(variant) = child.get(#variant_name_string) {
return Ok(Self::#variant_name(variant.try_to::<#field_type>()?));
}
}
}
} else {
let fields = fields.fields.iter().enumerate()
.map(|(k, (field, _))|{
let ident = format_ident!("__{k}");
let field_type = &field.ty;
(
quote!{#ident},

quote!{
let #ident = variant
.pop_front()
.ok_or(godot::builtin::VariantConversionError::MissingValue)?
.try_to::<#field_type>()?;
})
});
let (idents, set_idents): (Vec<_>, Vec<_>) = fields.unzip();

quote! {
let child = root.try_to::<godot::builtin::Dictionary>();
if let Ok(child) = child {
if let Some(variant) = child.get(#variant_name_string) {
let mut variant = variant.try_to::<godot::builtin::Array<godot::builtin::Variant>>()?;
#(#set_idents)*
return Ok(Self::#variant_name(#(#idents ,)*));
}
}
}
}
}
StructFields::Named(fields) => {
let fields = fields.fields.iter().map(|(field, _)| {
let field_name = &field.name;
let field_name_string = &field.name.to_string();
let field_type = &field.ty;
(
quote!{#field_name},
quote!{
let #field_name = variant.get(#field_name_string).ok_or(godot::builtin::VariantConversionError::MissingValue)?.try_to::<#field_type>()?;
}
)
});
let (fields, set_fields): (Vec<_>, Vec<_>) = fields.unzip();
quote! {
if let Ok(root) = root.try_to::<godot::builtin::Dictionary>() {
if let Some(variant) = root.get(#variant_name_string) {
let variant = variant.try_to::<godot::builtin::Dictionary>()?;
#(
#set_fields
)*
return Ok(Self::#variant_name{ #(#fields,)* });
}
}
}
}
};
matches = quote! {
#matches
#if_let_content
};
}
body = quote! {
#body
#matches
Err(godot::builtin::VariantConversionError::MissingValue)
};
}
}
_ => unreachable!(),
}

let gen = generic_params.as_ref().map(|x| x.as_inline_args());
Ok(quote! {
impl #generic_params godot::builtin::FromVariant for #name #gen #where_ {
fn try_from_variant(
variant: &godot::builtin::Variant
) -> Result<Self, godot::builtin::VariantConversionError> {
#body
}
}
})
}
Loading

0 comments on commit 34f0ffe

Please sign in to comment.