Skip to content

Commit 34f0ffe

Browse files
bors[bot]Astrale
and
Astrale
authored
Merge #246
246: Derive From Variant To Variant r=Bromeon a=astrale-sharp Closes #245 Co-authored-by: Astrale <[email protected]>
2 parents 3b30209 + cef7baa commit 34f0ffe

File tree

14 files changed

+657
-19
lines changed

14 files changed

+657
-19
lines changed

godot-core/src/builtin/array.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -713,7 +713,7 @@ impl<T: VariantMetadata> ToVariant for Array<T> {
713713
impl<T: VariantMetadata> FromVariant for Array<T> {
714714
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
715715
if variant.get_type() != Self::variant_type() {
716-
return Err(VariantConversionError);
716+
return Err(VariantConversionError::BadType);
717717
}
718718

719719
let array = unsafe {

godot-core/src/builtin/variant/impls.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ macro_rules! impl_variant_traits {
5858
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
5959
// Type check -- at the moment, a strict match is required.
6060
if variant.get_type() != Self::variant_type() {
61-
return Err(VariantConversionError)
61+
return Err(VariantConversionError::BadType)
6262
}
6363

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

@@ -251,7 +251,7 @@ impl<T: EngineEnum> ToVariant for T {
251251
impl<T: EngineEnum> FromVariant for T {
252252
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
253253
<i32 as FromVariant>::try_from_variant(variant)
254-
.and_then(|int| Self::try_from_ord(int).ok_or(VariantConversionError))
254+
.and_then(|int| Self::try_from_ord(int).ok_or(VariantConversionError::BadType))
255255
}
256256
}
257257

godot-core/src/builtin/variant/variant_traits.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,14 @@ pub trait ToVariant {
5050
// ----------------------------------------------------------------------------------------------------------------------------------------------
5151

5252
#[derive(Eq, PartialEq, Debug)]
53-
pub struct VariantConversionError;
54-
/*pub enum VariantConversionError {
53+
//pub struct VariantConversionError;
54+
pub enum VariantConversionError {
5555
/// Variant type does not match expected type
5656
BadType,
5757

5858
/// Variant value cannot be represented in target type
5959
BadValue,
60-
}*/
60+
61+
/// Variant value is missing a value for the target type
62+
MissingValue,
63+
}

godot-core/src/obj/gd.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -728,7 +728,7 @@ impl<T: GodotClass> FromVariant for Gd<T> {
728728
// TODO(#234) remove this cast when Godot stops allowing illegal conversions
729729
// (See https://github.com/godot-rust/gdext/issues/158)
730730
.and_then(|obj| obj.owned_cast().ok())
731-
.ok_or(VariantConversionError)
731+
.ok_or(VariantConversionError::BadType)
732732
}
733733
}
734734

godot-core/src/obj/instance_id.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ unsafe impl GodotFfi for InstanceId {
8686
impl FromVariant for InstanceId {
8787
fn try_from_variant(variant: &Variant) -> Result<Self, VariantConversionError> {
8888
i64::try_from_variant(variant)
89-
.and_then(|i| InstanceId::try_from_i64(i).ok_or(VariantConversionError))
89+
.and_then(|i| InstanceId::try_from_i64(i).ok_or(VariantConversionError::BadValue))
9090
}
9191
}
9292

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* This Source Code Form is subject to the terms of the Mozilla Public
3+
* License, v. 2.0. If a copy of the MPL was not distributed with this
4+
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
5+
*/
6+
7+
use crate::util::{decl_get_info, DeclInfo};
8+
use crate::ParseResult;
9+
use proc_macro2::TokenStream;
10+
use quote::{format_ident, quote};
11+
use venial::{Declaration, StructFields};
12+
13+
pub fn transform(decl: Declaration) -> ParseResult<TokenStream> {
14+
let DeclInfo {
15+
where_,
16+
generic_params,
17+
name,
18+
name_string,
19+
} = decl_get_info(&decl);
20+
let mut body = quote! {
21+
let root = variant.try_to::<godot::builtin::Dictionary>()?;
22+
let root = root.get(#name_string).ok_or(godot::builtin::VariantConversionError::BadType)?;
23+
};
24+
25+
match decl {
26+
Declaration::Struct(s) => match s.fields {
27+
venial::StructFields::Unit => {
28+
body = quote! {
29+
#body
30+
return Ok(Self);
31+
}
32+
}
33+
venial::StructFields::Tuple(fields) => {
34+
if fields.fields.len() == 1 {
35+
body = quote! {
36+
#body
37+
let root = root.try_to()?;
38+
Ok(Self(root))
39+
};
40+
} else {
41+
let ident_and_set = fields.fields.iter().enumerate().map(|(k, _)| {
42+
let ident = format_ident!("__{}", k);
43+
(
44+
ident.clone(),
45+
quote! {
46+
let #ident = root.pop_front().ok_or(godot::builtin::VariantConversionError::MissingValue)?;
47+
},
48+
49+
)
50+
});
51+
let (idents, ident_set): (Vec<_>, Vec<_>) = ident_and_set.unzip();
52+
body = quote! {
53+
#body
54+
let mut root = root.try_to::<godot::builtin::Array<godot::builtin::Variant>>()?;
55+
#(
56+
#ident_set
57+
58+
)*
59+
Ok(Self(
60+
#(#idents.try_to()?,)*
61+
))
62+
};
63+
}
64+
}
65+
venial::StructFields::Named(fields) => {
66+
let fields = fields.fields.iter().map(|(field, _)|{
67+
let ident = &field.name;
68+
let string_ident = &field.name.to_string();
69+
(
70+
quote!{
71+
let #ident = root.get(#string_ident).ok_or(godot::builtin::VariantConversionError::MissingValue)?;
72+
},
73+
74+
quote!{
75+
#ident :#ident.try_to()?
76+
}
77+
)
78+
79+
});
80+
let (set_idents, set_self): (Vec<_>, Vec<_>) = fields.unzip();
81+
body = quote! {
82+
#body
83+
let root = root.try_to::<godot::builtin::Dictionary>()?;
84+
#(
85+
#set_idents
86+
)*
87+
Ok(Self{ #(#set_self,)* })
88+
}
89+
}
90+
},
91+
Declaration::Enum(enum_) => {
92+
if enum_.variants.is_empty() {
93+
body = quote! {
94+
panic!();
95+
}
96+
} else {
97+
let mut matches = quote! {};
98+
for (enum_v, _) in &enum_.variants.inner {
99+
let variant_name = enum_v.name.clone();
100+
let variant_name_string = enum_v.name.to_string();
101+
let if_let_content = match &enum_v.contents {
102+
StructFields::Unit => quote! {
103+
let child = root.try_to::<String>();
104+
if child == Ok(String::from(#variant_name_string)) {
105+
return Ok(Self::#variant_name);
106+
}
107+
},
108+
StructFields::Tuple(fields) => {
109+
if fields.fields.len() == 1 {
110+
let (field, _) = fields.fields.first().unwrap();
111+
let field_type = &field.ty;
112+
quote! {
113+
let child = root.try_to::<godot::builtin::Dictionary>();
114+
if let Ok(child) = child {
115+
if let Some(variant) = child.get(#variant_name_string) {
116+
return Ok(Self::#variant_name(variant.try_to::<#field_type>()?));
117+
}
118+
}
119+
}
120+
} else {
121+
let fields = fields.fields.iter().enumerate()
122+
.map(|(k, (field, _))|{
123+
let ident = format_ident!("__{k}");
124+
let field_type = &field.ty;
125+
(
126+
quote!{#ident},
127+
128+
quote!{
129+
let #ident = variant
130+
.pop_front()
131+
.ok_or(godot::builtin::VariantConversionError::MissingValue)?
132+
.try_to::<#field_type>()?;
133+
})
134+
});
135+
let (idents, set_idents): (Vec<_>, Vec<_>) = fields.unzip();
136+
137+
quote! {
138+
let child = root.try_to::<godot::builtin::Dictionary>();
139+
if let Ok(child) = child {
140+
if let Some(variant) = child.get(#variant_name_string) {
141+
let mut variant = variant.try_to::<godot::builtin::Array<godot::builtin::Variant>>()?;
142+
#(#set_idents)*
143+
return Ok(Self::#variant_name(#(#idents ,)*));
144+
}
145+
}
146+
}
147+
}
148+
}
149+
StructFields::Named(fields) => {
150+
let fields = fields.fields.iter().map(|(field, _)| {
151+
let field_name = &field.name;
152+
let field_name_string = &field.name.to_string();
153+
let field_type = &field.ty;
154+
(
155+
quote!{#field_name},
156+
quote!{
157+
let #field_name = variant.get(#field_name_string).ok_or(godot::builtin::VariantConversionError::MissingValue)?.try_to::<#field_type>()?;
158+
}
159+
)
160+
});
161+
let (fields, set_fields): (Vec<_>, Vec<_>) = fields.unzip();
162+
quote! {
163+
if let Ok(root) = root.try_to::<godot::builtin::Dictionary>() {
164+
if let Some(variant) = root.get(#variant_name_string) {
165+
let variant = variant.try_to::<godot::builtin::Dictionary>()?;
166+
#(
167+
#set_fields
168+
)*
169+
return Ok(Self::#variant_name{ #(#fields,)* });
170+
}
171+
}
172+
}
173+
}
174+
};
175+
matches = quote! {
176+
#matches
177+
#if_let_content
178+
};
179+
}
180+
body = quote! {
181+
#body
182+
#matches
183+
Err(godot::builtin::VariantConversionError::MissingValue)
184+
};
185+
}
186+
}
187+
_ => unreachable!(),
188+
}
189+
190+
let gen = generic_params.as_ref().map(|x| x.as_inline_args());
191+
Ok(quote! {
192+
impl #generic_params godot::builtin::FromVariant for #name #gen #where_ {
193+
fn try_from_variant(
194+
variant: &godot::builtin::Variant
195+
) -> Result<Self, godot::builtin::VariantConversionError> {
196+
#body
197+
}
198+
}
199+
})
200+
}

0 commit comments

Comments
 (0)