Skip to content

Commit 94d7a45

Browse files
committed
feat(FromVariant): allow derived by uninhabitable enums
1 parent 3731a64 commit 94d7a45

File tree

3 files changed

+76
-46
lines changed

3 files changed

+76
-46
lines changed

gdnative-derive/src/variant/from.rs

Lines changed: 48 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,6 @@ pub(crate) fn expand_from_variant(derive_data: DeriveData) -> Result<TokenStream
3030
}
3131
}
3232
Repr::Enum(variants) => {
33-
if variants.is_empty() {
34-
return Err(syn::Error::new(
35-
ident.span(),
36-
"cannot derive FromVariant for an uninhabited enum",
37-
));
38-
}
39-
4033
let var_input_ident = Ident::new("__enum_variant", Span::call_site());
4134

4235
let var_ident_strings: Vec<String> = variants
@@ -61,46 +54,55 @@ pub(crate) fn expand_from_variant(derive_data: DeriveData) -> Result<TokenStream
6154

6255
let var_input_ident_iter = std::iter::repeat(&var_input_ident);
6356

57+
// Return `FromVariantError` if input is an uninhabitable enum
58+
let early_return = variants.is_empty().then(|| {
59+
quote! {
60+
return Err(FVE::UnknownEnumVariant {
61+
variant: __key,
62+
expected: &[],
63+
});
64+
}
65+
});
66+
6467
quote! {
65-
{
66-
let __dict = ::gdnative::core_types::Dictionary::from_variant(#input_ident)
67-
.map_err(|__err| FVE::InvalidEnumRepr {
68-
expected: VariantEnumRepr::ExternallyTagged,
69-
error: std::boxed::Box::new(__err),
70-
})?;
71-
72-
let __keys = __dict.keys();
73-
if __keys.len() != 1 {
74-
Err(FVE::InvalidEnumRepr {
75-
expected: VariantEnumRepr::ExternallyTagged,
76-
error: std::boxed::Box::new(FVE::InvalidLength {
77-
expected: 1,
78-
len: __keys.len() as usize,
79-
}),
80-
})
81-
}
82-
else {
83-
let __key = String::from_variant(&__keys.get(0))
84-
.map_err(|__err| FVE::InvalidEnumRepr {
85-
expected: VariantEnumRepr::ExternallyTagged,
86-
error: std::boxed::Box::new(__err),
87-
})?;
88-
match __key.as_str() {
89-
#(
90-
#ref_var_ident_string_literals => {
91-
let #var_input_ident_iter = &__dict.get_or_nil(&__keys.get(0));
92-
(#var_from_variants).map_err(|err| FVE::InvalidEnumVariant {
93-
variant: #ref_var_ident_string_literals,
94-
error: std::boxed::Box::new(err),
95-
})
96-
},
97-
)*
98-
variant => Err(FVE::UnknownEnumVariant {
99-
variant: variant.to_string(),
100-
expected: &[#(#ref_var_ident_string_literals),*],
101-
}),
102-
}
103-
}
68+
let __dict = ::gdnative::core_types::Dictionary::from_variant(#input_ident)
69+
.map_err(|__err| FVE::InvalidEnumRepr {
70+
expected: VariantEnumRepr::ExternallyTagged,
71+
error: std::boxed::Box::new(__err),
72+
})?;
73+
let __keys = __dict.keys();
74+
if __keys.len() != 1 {
75+
return Err(FVE::InvalidEnumRepr {
76+
expected: VariantEnumRepr::ExternallyTagged,
77+
error: std::boxed::Box::new(FVE::InvalidLength {
78+
expected: 1,
79+
len: __keys.len() as usize,
80+
}),
81+
})
82+
}
83+
84+
let __key = String::from_variant(&__keys.get(0))
85+
.map_err(|__err| FVE::InvalidEnumRepr {
86+
expected: VariantEnumRepr::ExternallyTagged,
87+
error: std::boxed::Box::new(__err),
88+
})?;
89+
90+
#early_return
91+
92+
match __key.as_str() {
93+
#(
94+
#ref_var_ident_string_literals => {
95+
let #var_input_ident_iter = &__dict.get_or_nil(&__keys.get(0));
96+
(#var_from_variants).map_err(|err| FVE::InvalidEnumVariant {
97+
variant: #ref_var_ident_string_literals,
98+
error: std::boxed::Box::new(err),
99+
})
100+
},
101+
)*
102+
variant => Err(FVE::UnknownEnumVariant {
103+
variant: variant.to_string(),
104+
expected: &[#(#ref_var_ident_string_literals),*],
105+
}),
104106
}
105107
}
106108
}

gdnative-derive/src/variant/mod.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,17 @@ pub(crate) fn derive_from_variant(derive_input: DeriveInput) -> Result<TokenStre
107107
let variant = parse_derive_input(derive_input, &bound, Direction::From);
108108
from::expand_from_variant(variant?)
109109
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
115+
#[test]
116+
fn derive_from_variant_on_uninhabitable_enums() {
117+
let input = parse_quote! {
118+
enum Test {}
119+
};
120+
121+
derive_from_variant(input).unwrap();
122+
}
123+
}

test/src/test_derive.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use std::cell::{self, Cell, RefCell};
2+
use std::collections::HashMap;
23
use std::rc::Rc;
34

45
use gdnative::export::Property;
@@ -144,6 +145,19 @@ crate::godot_itest! { test_derive_to_variant {
144145
Ok(ToVarTuple::<f64, i128>(1, 0, false)),
145146
ToVarTuple::from_variant(&variant)
146147
);
148+
149+
// Derive on uninhabitable enum results an error
150+
#[derive(Debug, PartialEq, FromVariant)]
151+
enum NoVariant {}
152+
153+
let input = HashMap::from_iter([("foo", "bar")]).to_variant();
154+
assert_eq!(
155+
NoVariant::from_variant(&input),
156+
Err(FromVariantError::UnknownEnumVariant {
157+
variant: "foo".into(),
158+
expected: &[]
159+
})
160+
);
147161
}}
148162

149163
// ----------------------------------------------------------------------------------------------------------------------------------------------

0 commit comments

Comments
 (0)