Skip to content

Commit acfa785

Browse files
committed
Treat VariantType and VariantOperator like regular engine enums
Avoids two separate code paths for enum codegen, and makes sure the generated API is the same (e.g. EngineEnum, IndexEnum traits). VariantType requires still a bit of special handling, as its definition must be available in godot-ffi, while the trait impls are only written in godot-core. Thus, the codegen is split. Retains deprecation for old VariantType and VariantOperator enumerators.
1 parent 89f5052 commit acfa785

File tree

16 files changed

+226
-202
lines changed

16 files changed

+226
-202
lines changed

godot-codegen/src/generator/central_files.rs

Lines changed: 38 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,18 @@
88
use crate::context::Context;
99
use crate::conv;
1010
use crate::generator::{enums, gdext_build_struct};
11-
use crate::models::domain::{Enumerator, ExtensionApi};
11+
use crate::models::domain::ExtensionApi;
1212
use crate::util::ident;
13-
use proc_macro2::{Ident, Literal, TokenStream};
13+
use proc_macro2::{Ident, TokenStream};
1414
use quote::{format_ident, quote, ToTokens};
1515

16-
pub fn make_sys_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStream {
17-
let VariantEnums {
18-
variant_ty_enumerators_pascal,
19-
variant_ty_enumerators_ord,
20-
variant_op_enumerators_pascal,
21-
variant_op_enumerators_ord,
22-
..
23-
} = make_variant_enums(api, ctx);
24-
16+
pub fn make_sys_central_code(api: &ExtensionApi) -> TokenStream {
2517
let build_config_struct = gdext_build_struct::make_gdext_build_struct(&api.godot_version);
18+
let (variant_type_enum, variant_type_deprecated_enumerators) =
19+
make_variant_type_enum(api, true);
2620
let [opaque_32bit, opaque_64bit] = make_opaque_types(api);
2721

2822
quote! {
29-
use crate::{GDExtensionVariantOperator, GDExtensionVariantType};
30-
3123
#[cfg(target_pointer_width = "32")]
3224
pub mod types {
3325
#(#opaque_32bit)*
@@ -37,66 +29,24 @@ pub fn make_sys_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStre
3729
#(#opaque_64bit)*
3830
}
3931

40-
4132
// ----------------------------------------------------------------------------------------------------------------------------------------------
4233

4334
#build_config_struct
44-
45-
// ----------------------------------------------------------------------------------------------------------------------------------------------
46-
47-
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
48-
#[repr(i32)]
49-
pub enum VariantType {
50-
Nil = 0,
51-
#(
52-
#variant_ty_enumerators_pascal = #variant_ty_enumerators_ord,
53-
)*
54-
}
35+
#variant_type_enum
5536

5637
impl VariantType {
38+
// This will need refactoring if VariantType is changed to a real enum.
5739
#[doc(hidden)]
58-
pub fn from_sys(enumerator: GDExtensionVariantType) -> Self {
59-
// Annoying, but only stable alternative is transmute(), which dictates enum size.
60-
match enumerator {
61-
0 => Self::Nil,
62-
#(
63-
#variant_ty_enumerators_ord => Self::#variant_ty_enumerators_pascal,
64-
)*
65-
_ => unreachable!("invalid variant type {}", enumerator)
66-
}
40+
pub fn from_sys(enumerator: crate::GDExtensionVariantType) -> Self {
41+
Self { ord: enumerator as i32 }
6742
}
6843

6944
#[doc(hidden)]
70-
pub fn sys(self) -> GDExtensionVariantType {
71-
self as _
45+
pub fn sys(self) -> crate::GDExtensionVariantType {
46+
self.ord as _
7247
}
73-
}
7448

75-
// ----------------------------------------------------------------------------------------------------------------------------------------------
76-
77-
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
78-
#[repr(i32)]
79-
pub enum VariantOperator {
80-
#(
81-
#variant_op_enumerators_pascal = #variant_op_enumerators_ord,
82-
)*
83-
}
84-
85-
impl VariantOperator {
86-
#[doc(hidden)]
87-
pub fn from_sys(enumerator: GDExtensionVariantOperator) -> Self {
88-
match enumerator {
89-
#(
90-
#variant_op_enumerators_ord => Self::#variant_op_enumerators_pascal,
91-
)*
92-
_ => unreachable!("invalid variant operator {}", enumerator)
93-
}
94-
}
95-
96-
#[doc(hidden)]
97-
pub fn sys(self) -> GDExtensionVariantOperator {
98-
self as _
99-
}
49+
#variant_type_deprecated_enumerators
10050
}
10151
}
10252
}
@@ -109,6 +59,7 @@ pub fn make_core_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStr
10959
} = make_variant_enums(api, ctx);
11060

11161
let global_enum_defs = make_global_enums(api);
62+
let variant_type_traits = make_variant_type_enum(api, false).0;
11263

11364
// TODO impl Clone, Debug, PartialEq, PartialOrd, Hash for VariantDispatch
11465
// TODO could use try_to().unwrap_unchecked(), since type is already verified. Also directly overload from_variant().
@@ -118,6 +69,9 @@ pub fn make_core_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStr
11869
use crate::engine::Object;
11970
use crate::obj::Gd;
12071

72+
// Remaining trait impls for sys::VariantType (traits only defined in godot-core).
73+
#variant_type_traits
74+
12175
#[allow(dead_code)]
12276
pub enum VariantDispatch {
12377
Nil,
@@ -134,6 +88,9 @@ pub fn make_core_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStr
13488
VariantType::#variant_ty_enumerators_pascal
13589
=> Self::#variant_ty_enumerators_pascal(variant.to::<#variant_ty_enumerators_rust>()),
13690
)*
91+
92+
// Panic can be removed as soon as VariantType is a proper, non-exhaustive enum.
93+
_ => panic!("Variant type not supported: {:?}", variant.get_type()),
13794
}
13895
}
13996
}
@@ -168,19 +125,6 @@ pub fn make_core_central_code(api: &ExtensionApi, ctx: &mut Context) -> TokenStr
168125
struct VariantEnums {
169126
variant_ty_enumerators_pascal: Vec<Ident>,
170127
variant_ty_enumerators_rust: Vec<TokenStream>,
171-
variant_ty_enumerators_ord: Vec<Literal>,
172-
variant_op_enumerators_pascal: Vec<Ident>,
173-
variant_op_enumerators_ord: Vec<Literal>,
174-
}
175-
176-
fn collect_variant_operators(api: &ExtensionApi) -> Vec<&Enumerator> {
177-
let variant_operator_enum = api
178-
.global_enums
179-
.iter()
180-
.find(|e| &e.name == "VariantOperator") // in JSON: "Variant.Operator"
181-
.expect("missing enum for VariantOperator in JSON");
182-
183-
variant_operator_enum.enumerators.iter().collect()
184128
}
185129

186130
fn make_opaque_types(api: &ExtensionApi) -> [Vec<TokenStream>; 2] {
@@ -208,49 +152,27 @@ fn make_opaque_type(godot_original_name: &str, size: usize) -> TokenStream {
208152
}
209153

210154
fn make_variant_enums(api: &ExtensionApi, ctx: &mut Context) -> VariantEnums {
211-
let variant_operators = collect_variant_operators(api);
212-
213155
// Generate builtin methods, now with info for all types available.
214156
// Separate vectors because that makes usage in quote! easier.
215157
let len = api.builtins.len();
216158

217159
let mut result = VariantEnums {
218160
variant_ty_enumerators_pascal: Vec::with_capacity(len),
219161
variant_ty_enumerators_rust: Vec::with_capacity(len),
220-
variant_ty_enumerators_ord: Vec::with_capacity(len),
221-
variant_op_enumerators_pascal: Vec::new(),
222-
variant_op_enumerators_ord: Vec::new(),
223162
};
224163

225164
// Note: NIL is not part of this iteration, it will be added manually.
226165
for builtin in api.builtins.iter() {
227166
let original_name = builtin.godot_original_name();
228167
let rust_ty = conv::to_rust_type(original_name, None, ctx);
229168
let pascal_case = conv::to_pascal_case(original_name);
230-
let ord = builtin.unsuffixed_ord_lit();
231169

232170
result
233171
.variant_ty_enumerators_pascal
234172
.push(ident(&pascal_case));
235173
result
236174
.variant_ty_enumerators_rust
237175
.push(rust_ty.to_token_stream());
238-
result.variant_ty_enumerators_ord.push(ord);
239-
}
240-
241-
for op in variant_operators {
242-
let pascal_name = conv::to_pascal_case(&op.name.to_string());
243-
244-
let enumerator_name = if pascal_name == "Module" {
245-
ident("Modulo")
246-
} else {
247-
ident(&pascal_name)
248-
};
249-
250-
result.variant_op_enumerators_pascal.push(enumerator_name);
251-
result
252-
.variant_op_enumerators_ord
253-
.push(op.value.unsuffixed_lit());
254176
}
255177

256178
result
@@ -260,8 +182,8 @@ fn make_global_enums(api: &ExtensionApi) -> Vec<TokenStream> {
260182
let mut global_enum_defs = vec![];
261183

262184
for enum_ in api.global_enums.iter() {
263-
// Skip those enums which are already manually handled.
264-
if enum_.name == "VariantType" || enum_.name == "VariantOperator" {
185+
// Skip VariantType, which is already defined in godot-ffi.
186+
if enum_.name == "VariantType" {
265187
continue;
266188
}
267189

@@ -271,3 +193,20 @@ fn make_global_enums(api: &ExtensionApi) -> Vec<TokenStream> {
271193

272194
global_enum_defs
273195
}
196+
197+
fn make_variant_type_enum(api: &ExtensionApi, is_definition: bool) -> (TokenStream, TokenStream) {
198+
let variant_type_enum = api
199+
.global_enums
200+
.iter()
201+
.find(|e| e.name == "VariantType")
202+
.expect("missing VariantType enum in API JSON");
203+
204+
let define_enum = is_definition;
205+
let define_traits = !is_definition;
206+
207+
let enum_definition =
208+
enums::make_enum_definition_with(variant_type_enum, define_enum, define_traits);
209+
let deprecated_enumerators = enums::make_deprecated_enumerators(variant_type_enum);
210+
211+
(enum_definition, deprecated_enumerators)
212+
}

godot-codegen/src/generator/enums.rs

Lines changed: 84 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
//! See also models/domain/enums.rs for other enum-related methods.
1111
1212
use crate::models::domain::{Enum, Enumerator};
13+
use crate::{conv, util};
1314
use proc_macro2::TokenStream;
1415
use quote::{quote, ToTokens};
1516

@@ -25,6 +26,14 @@ pub fn make_enums(enums: &[Enum], cfg_attributes: &TokenStream) -> TokenStream {
2526
///
2627
/// This will also implement all relevant traits and generate appropriate constants for each enumerator.
2728
pub fn make_enum_definition(enum_: &Enum) -> TokenStream {
29+
make_enum_definition_with(enum_, true, true)
30+
}
31+
32+
pub fn make_enum_definition_with(
33+
enum_: &Enum,
34+
define_enum: bool,
35+
define_traits: bool,
36+
) -> TokenStream {
2837
// Things needed for the type definition
2938
let derives = enum_.derives();
3039
let enum_doc = make_enum_doc(enum_);
@@ -36,47 +45,65 @@ pub fn make_enum_definition(enum_: &Enum) -> TokenStream {
3645
.iter()
3746
.map(|enumerator| make_enumerator_definition(enumerator, name.to_token_stream()));
3847

39-
// Trait implementations
40-
let engine_trait_impl = make_enum_engine_trait_impl(enum_);
41-
let index_enum_impl = make_enum_index_impl(enum_);
42-
let bitwise_impls = make_enum_bitwise_operators(enum_);
43-
4448
// Various types
4549
let ord_type = enum_.ord_type();
4650
let engine_trait = enum_.engine_trait();
4751

48-
quote! {
49-
#[repr(transparent)]
50-
#[derive( #( #derives ),* )]
51-
#( #[doc = #enum_doc] )*
52-
pub struct #name {
53-
ord: #ord_type
54-
}
52+
let definition = define_enum.then(|| {
53+
// Workaround because traits are defined in separate crate, but need access to field `ord`.
54+
let vis = (!define_traits).then(|| {
55+
quote! {
56+
#[doc(hidden)] pub
57+
}
58+
});
59+
60+
quote! {
61+
#[repr(transparent)]
62+
#[derive( #( #derives ),* )]
63+
#( #[doc = #enum_doc] )*
64+
pub struct #name {
65+
#vis ord: #ord_type
66+
}
5567

56-
impl #name {
57-
#( #enumerators )*
68+
impl #name {
69+
#( #enumerators )*
70+
}
5871
}
72+
});
5973

60-
#engine_trait_impl
61-
#index_enum_impl
62-
#bitwise_impls
74+
let traits = define_traits.then(|| {
75+
// Trait implementations
76+
let engine_trait_impl = make_enum_engine_trait_impl(enum_);
77+
let index_enum_impl = make_enum_index_impl(enum_);
78+
let bitwise_impls = make_enum_bitwise_operators(enum_);
6379

64-
impl crate::builtin::meta::GodotConvert for #name {
65-
type Via = #ord_type;
66-
}
80+
quote! {
81+
#engine_trait_impl
82+
#index_enum_impl
83+
#bitwise_impls
6784

68-
impl crate::builtin::meta::ToGodot for #name {
69-
fn to_godot(&self) -> Self::Via {
70-
<Self as #engine_trait>::ord(*self)
85+
impl crate::builtin::meta::GodotConvert for #name {
86+
type Via = #ord_type;
7187
}
72-
}
7388

74-
impl crate::builtin::meta::FromGodot for #name {
75-
fn try_from_godot(via: Self::Via) -> std::result::Result<Self, crate::builtin::meta::ConvertError> {
76-
<Self as #engine_trait>::try_from_ord(via)
77-
.ok_or_else(|| crate::builtin::meta::FromGodotError::InvalidEnum.into_error(via))
89+
impl crate::builtin::meta::ToGodot for #name {
90+
fn to_godot(&self) -> Self::Via {
91+
<Self as #engine_trait>::ord(*self)
92+
}
93+
}
94+
95+
impl crate::builtin::meta::FromGodot for #name {
96+
fn try_from_godot(via: Self::Via) -> std::result::Result<Self, crate::builtin::meta::ConvertError> {
97+
<Self as #engine_trait>::try_from_ord(via)
98+
.ok_or_else(|| crate::builtin::meta::FromGodotError::InvalidEnum.into_error(via))
99+
}
78100
}
79101
}
102+
});
103+
104+
quote! {
105+
#definition
106+
#traits
80107
}
81108
}
82109

@@ -203,3 +230,32 @@ fn make_enumerator_definition(enumerator: &Enumerator, enum_type: TokenStream) -
203230
};
204231
}
205232
}
233+
234+
pub(crate) fn make_deprecated_enumerators(enum_: &Enum) -> TokenStream {
235+
let enum_name = &enum_.name;
236+
let deprecated_enumerators = enum_.enumerators.iter().filter_map(|enumerator| {
237+
let Enumerator { name, .. } = enumerator;
238+
239+
if name == "MAX" {
240+
return None;
241+
}
242+
243+
let converted = conv::to_pascal_case(&name.to_string())
244+
.replace("2d", "2D")
245+
.replace("3d", "3D");
246+
247+
let pascal_name = util::ident(&converted);
248+
let msg = format!("Use `{enum_name}::{name}` instead.");
249+
250+
let decl = quote! {
251+
#[deprecated = #msg]
252+
pub const #pascal_name: #enum_name = Self::#name;
253+
};
254+
255+
Some(decl)
256+
});
257+
258+
quote! {
259+
#( #deprecated_enumerators )*
260+
}
261+
}

godot-codegen/src/generator/mod.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,10 @@ pub fn generate_sys_module_file(sys_gen_path: &Path, submit_fn: &mut SubmitFn) {
5555

5656
pub fn generate_sys_central_file(
5757
api: &ExtensionApi,
58-
ctx: &mut Context,
5958
sys_gen_path: &Path,
6059
submit_fn: &mut SubmitFn,
6160
) {
62-
let sys_code = central_files::make_sys_central_code(api, ctx);
61+
let sys_code = central_files::make_sys_central_code(api);
6362

6463
submit_fn(sys_gen_path.join("central.rs"), sys_code);
6564
}

0 commit comments

Comments
 (0)