99//!
1010//! See also models/domain/enums.rs for other enum-related methods.
1111
12- use crate :: models:: domain:: { Enum , Enumerator } ;
12+ use crate :: models:: domain:: { Enum , Enumerator , EnumeratorValue } ;
1313use crate :: { conv, util} ;
1414use proc_macro2:: TokenStream ;
1515use quote:: { quote, ToTokens } ;
@@ -34,46 +34,67 @@ pub fn make_enum_definition_with(
3434 define_enum : bool ,
3535 define_traits : bool ,
3636) -> TokenStream {
37+ assert ! (
38+ !( enum_. is_bitfield && enum_. is_exhaustive) ,
39+ "bitfields cannot be marked exhaustive"
40+ ) ;
41+
3742 // Things needed for the type definition
3843 let derives = enum_. derives ( ) ;
3944 let enum_doc = make_enum_doc ( enum_) ;
4045 let name = & enum_. name ;
4146
4247 // Values
43- let enumerators = enum_
44- . enumerators
45- . iter ( )
46- . map ( |enumerator| make_enumerator_definition ( enumerator, name. to_token_stream ( ) ) ) ;
48+ let enumerators = enum_. enumerators . iter ( ) . map ( |enumerator| {
49+ make_enumerator_definition ( enumerator, name. to_token_stream ( ) , !enum_. is_exhaustive )
50+ } ) ;
4751
4852 // Various types
4953 let ord_type = enum_. ord_type ( ) ;
5054 let engine_trait = enum_. engine_trait ( ) ;
5155
52- let definition = define_enum. then ( || {
53- let debug_impl = make_enum_debug_impl ( enum_) ;
54-
55- // Workaround because traits are defined in separate crate, but need access to field `ord`.
56- let vis = ( !define_traits) . then ( || {
56+ let definition = if define_enum {
57+ // Exhaustive enums are declared as Rust enums.
58+ if enum_. is_exhaustive {
5759 quote ! {
58- #[ doc( hidden) ] pub
60+ #[ repr( i32 ) ]
61+ #[ derive( Debug , #( #derives ) , * ) ]
62+ #( #[ doc = #enum_doc] ) *
63+ ///
64+ /// This enum is exhaustive; you should not expect future Godot versions to add new enumerators.
65+ #[ allow( non_camel_case_types) ]
66+ pub enum #name {
67+ #( #enumerators ) *
68+ }
5969 }
60- } ) ;
70+ }
71+ //
72+ // Non-exhaustive enums are declared as newtype structs with associated constants.
73+ else {
74+ // Workaround because traits are defined in separate crate, but need access to field `ord`.
75+ let vis = ( !define_traits) . then ( || {
76+ quote ! { #[ doc( hidden) ] pub }
77+ } ) ;
78+
79+ let debug_impl = make_enum_debug_impl ( enum_) ;
80+ quote ! {
81+ #[ repr( transparent) ]
82+ #[ derive( #( #derives ) , * ) ]
83+ #( #[ doc = #enum_doc] ) *
84+ pub struct #name {
85+ #vis ord: #ord_type
86+ }
6187
62- quote ! {
63- #[ repr( transparent) ]
64- #[ derive( #( #derives ) , * ) ]
65- #( #[ doc = #enum_doc] ) *
66- pub struct #name {
67- #vis ord: #ord_type
68- }
88+ impl #name {
89+ #( #enumerators ) *
90+ }
6991
70- impl #name {
71- #( #enumerators ) *
92+ #debug_impl
7293 }
73-
74- #debug_impl
7594 }
76- } ) ;
95+ } else {
96+ TokenStream :: new ( )
97+ } ;
7798
7899 let traits = define_traits. then ( || {
79100 // Trait implementations
@@ -185,6 +206,36 @@ fn make_enum_engine_trait_impl(enum_: &Enum) -> TokenStream {
185206 }
186207 }
187208 }
209+ } else if enum_. is_exhaustive {
210+ let enumerators = enum_. enumerators . iter ( ) . map ( |enumerator| {
211+ let Enumerator {
212+ name,
213+ value : EnumeratorValue :: Enum ( ord) ,
214+ ..
215+ } = enumerator
216+ else {
217+ panic ! ( "exhaustive enum contains bitfield enumerators" )
218+ } ;
219+
220+ quote ! {
221+ #ord => Some ( Self :: #name) ,
222+ }
223+ } ) ;
224+
225+ quote ! {
226+ impl #engine_trait for #name {
227+ fn try_from_ord( ord: i32 ) -> Option <Self > {
228+ match ord {
229+ #( #enumerators ) *
230+ _ => None ,
231+ }
232+ }
233+
234+ fn ord( self ) -> i32 {
235+ self as i32
236+ }
237+ }
238+ }
188239 } else {
189240 let unique_ords = enum_. unique_ords ( ) . expect ( "self is an enum" ) ;
190241
@@ -238,13 +289,21 @@ fn make_enum_doc(enum_: &Enum) -> Vec<String> {
238289 docs
239290}
240291
241- /// Creates a `const` definition for `enumerator` of the type `enum_type`.
292+ /// Creates a definition for `enumerator` of the type `enum_type`.
242293///
243- /// That is, it'll be a definition like
294+ /// If `as_constant` is true, it will be a `const` definition like:
295+ /// ```ignore
296+ /// pub const NAME: enum_type = ord;
297+ /// ```
298+ /// Otherwise, it will be a regular enum variant like:
244299/// ```ignore
245- /// pub const NAME: enum_type = ..;
300+ /// NAME = ord,
246301/// ```
247- fn make_enumerator_definition ( enumerator : & Enumerator , enum_type : TokenStream ) -> TokenStream {
302+ fn make_enumerator_definition (
303+ enumerator : & Enumerator ,
304+ enum_type : TokenStream ,
305+ as_constant : bool ,
306+ ) -> TokenStream {
248307 let Enumerator {
249308 name,
250309 godot_name,
@@ -262,11 +321,18 @@ fn make_enumerator_definition(enumerator: &Enumerator, enum_type: TokenStream) -
262321 TokenStream :: new ( )
263322 } ;
264323
265- quote ! {
266- #docs
267- pub const #name: #enum_type = #enum_type {
268- ord: #value
269- } ;
324+ if as_constant {
325+ quote ! {
326+ #docs
327+ pub const #name: #enum_type = #enum_type {
328+ ord: #value
329+ } ;
330+ }
331+ } else {
332+ quote ! {
333+ #docs
334+ #name = #value,
335+ }
270336 }
271337}
272338
0 commit comments