Skip to content

Commit 3612631

Browse files
committed
EngineBitfield::all_constants()
1 parent a2a08d2 commit 3612631

File tree

6 files changed

+92
-41
lines changed

6 files changed

+92
-41
lines changed

godot-codegen/src/generator/enums.rs

Lines changed: 29 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,8 @@ fn make_enum_engine_trait_impl(enum_: &Enum, enum_bitmask: Option<&RustTy>) -> T
227227
if enum_.is_bitfield {
228228
// Bitfields: u64, assume any combination is valid.
229229

230+
let constants_function = make_all_constants_function(enum_);
231+
230232
quote! {
231233
// We may want to add this in the future.
232234
//
@@ -242,6 +244,8 @@ fn make_enum_engine_trait_impl(enum_: &Enum, enum_bitmask: Option<&RustTy>) -> T
242244
fn ord(self) -> u64 {
243245
self.ord
244246
}
247+
248+
#constants_function
245249
}
246250
}
247251
} else if enum_.is_exhaustive {
@@ -332,33 +336,49 @@ fn make_enum_values_and_constants_functions(enum_: &Enum) -> TokenStream {
332336
let name = &enum_.name;
333337

334338
let mut distinct_values = Vec::new();
335-
let mut all_constants = Vec::new();
336339
let mut seen_ordinals = HashSet::new();
337340

338341
for (index, enumerator) in enum_.enumerators.iter().enumerate() {
339342
let constant = &enumerator.name;
340-
let rust_name = enumerator.name.to_string();
341-
let godot_name = enumerator.godot_name.to_string();
342343
let ordinal = &enumerator.value;
343344

344-
// all_constants() includes every constant unconditionally.
345-
all_constants.push(quote! {
346-
crate::meta::inspect::EnumConstant::new(#rust_name, #godot_name, #ordinal, #name::#constant)
347-
});
348-
349345
// values() contains value only if distinct (first time seen) and not MAX.
350346
if enum_.max_index != Some(index) && seen_ordinals.insert(ordinal.clone()) {
351347
distinct_values.push(quote! { #name::#constant });
352348
}
353349
}
354350

355-
quote! {
351+
let values_function = quote! {
356352
fn values() -> &'static [Self] {
357353
&[
358354
#( #distinct_values ),*
359355
]
360356
}
357+
};
358+
359+
let all_constants_function = make_all_constants_function(enum_);
361360

361+
quote! {
362+
#values_function
363+
#all_constants_function
364+
}
365+
}
366+
367+
/// Creates a shared `all_constants()` implementation for enums and bitfields.
368+
fn make_all_constants_function(enum_: &Enum) -> TokenStream {
369+
let name = &enum_.name;
370+
371+
let all_constants = enum_.enumerators.iter().map(|enumerator| {
372+
let ident = &enumerator.name;
373+
let rust_name = enumerator.name.to_string();
374+
let godot_name = enumerator.godot_name.to_string();
375+
376+
quote! {
377+
crate::meta::inspect::EnumConstant::new(#rust_name, #godot_name, #name::#ident)
378+
}
379+
});
380+
381+
quote! {
362382
fn all_constants() -> &'static [crate::meta::inspect::EnumConstant<#name>] {
363383
const {
364384
&[

godot-core/src/builtin/vectors/vector_axis.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ macro_rules! impl_vector_axis_enum {
6969
EnumConstant::new(
7070
stringify!($axis),
7171
concat!("AXIS_", stringify!($axis)),
72-
$AxisEnum::$axis as i32,
7372
$AxisEnum::$axis
7473
),
7574
)+

godot-core/src/meta/inspect.rs

Lines changed: 12 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,34 +7,32 @@
77

88
//! Introspection metadata for Godot engine types.
99
10-
/// Metadata for a single enum constant.
10+
/// Metadata for a single enum or bitfield constant.
11+
///
12+
/// Returned by [`EngineEnum::all_constants()`][crate::obj::EngineEnum::all_constants] and
13+
/// [`EngineBitfield::all_constants()`][crate::obj::EngineBitfield::all_constants].
1114
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1215
pub struct EnumConstant<T: Copy + 'static> {
1316
rust_name: &'static str,
1417
godot_name: &'static str,
15-
ord: i32,
1618
value: T,
1719
}
1820

1921
impl<T: Copy + 'static> EnumConstant<T> {
2022
/// Creates a new enum constant metadata entry.
21-
pub(crate) const fn new(
22-
rust_name: &'static str,
23-
godot_name: &'static str,
24-
ord: i32,
25-
value: T,
26-
) -> Self {
23+
pub(crate) const fn new(rust_name: &'static str, godot_name: &'static str, value: T) -> Self {
2724
Self {
2825
rust_name,
2926
godot_name,
30-
ord,
3127
value,
3228
}
3329
}
3430

35-
/// Rust name of the enum variant, usually without Prefix (e.g. `"ESCAPE"` for `Key::ESCAPE`).
31+
/// Rust name of the constant, usually without prefix (e.g. `"ESCAPE"` for `Key::ESCAPE`).
3632
///
37-
/// This is returned by [`EngineEnum::as_str()`](crate::obj::EngineEnum::as_str()).
33+
/// For enums, this is the value returned by [`EngineEnum::as_str()`](crate::obj::EngineEnum::as_str()) **if the value is unique.**
34+
/// If multiple enum values share the same ordinal, then this function will return each one separately, while `as_str()` will return the
35+
/// first one.
3836
pub const fn rust_name(&self) -> &'static str {
3937
self.rust_name
4038
}
@@ -44,12 +42,9 @@ impl<T: Copy + 'static> EnumConstant<T> {
4442
self.godot_name
4543
}
4644

47-
/// Ordinal value of this enum variant.
48-
pub const fn ord(&self) -> i32 {
49-
self.ord
50-
}
51-
52-
/// The enum value itself.
45+
/// The Rust value itself.
46+
///
47+
/// Use `value().ord()` to get the ordinal value.
5348
pub const fn value(&self) -> T {
5449
self.value
5550
}

godot-core/src/obj/traits.rs

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,10 @@ pub trait EngineEnum: Copy {
190190

191191
/// The name of the enumerator, as it appears in Rust.
192192
///
193-
/// Note that **this may not match the Rust constant name!** In case of multiple constants with the same ordinal value, this method returns
194-
/// the first one in the order of definition. For example, `LayoutDirection::LOCALE.as_str()` (ord 1) returns `"APPLICATION_LOCALE"`, because
195-
/// that happens to be the first constant with ordinal `1`. See [`all_constants()`][Self::all_constants] for a more robust and general
196-
/// approach to introspection of enum constants.
193+
/// Note that **this may not match the Rust constant name.** In case of multiple constants with the same ordinal value, this method returns
194+
/// the first one in the order of definition. For example, [`LayoutDirection::LOCALE.as_str()`][crate::classes::window::LayoutDirection::LOCALE]
195+
/// (ord 1) returns `"APPLICATION_LOCALE"`, because that happens to be the first constant with ordinal `1`.
196+
/// See [`all_constants()`][Self::all_constants] for a more robust and general approach to introspection of enum constants.
197197
///
198198
/// If the value does not match one of the known enumerators, the empty string is returned.
199199
fn as_str(&self) -> &'static str;
@@ -205,9 +205,10 @@ pub trait EngineEnum: Copy {
205205

206206
/// Returns a slice of distinct enum values.
207207
///
208-
/// This excludes MAX constants and deduplicates aliases, providing only meaningful enum values.
208+
/// This excludes `MAX` constants at the end (existing only to express the number of enumerators) and deduplicates aliases,
209+
/// providing only meaningful enum values. See [`all_constants()`][Self::all_constants] for a complete list of all constants.
209210
///
210-
/// This enables iteration over distinct enum variants:
211+
/// Enables iteration over distinct enum variants:
211212
/// ```no_run
212213
/// use godot::classes::window;
213214
/// use godot::obj::EngineEnum;
@@ -220,18 +221,19 @@ pub trait EngineEnum: Copy {
220221

221222
/// Returns metadata for all enum constants.
222223
///
223-
/// This includes all constants as they appear in the enum definition, including duplicates and MAX constants.
224+
/// This includes all constants as they appear in the enum definition, including duplicates and `MAX` constants.
225+
/// For a list of useful, distinct values, use [`values()`][Self::values].
224226
///
225-
/// This enables complete introspection and debugging:
227+
/// Enables introspection of available constants:
226228
/// ```no_run
227229
/// use godot::classes::window;
228230
/// use godot::obj::EngineEnum;
229231
///
230232
/// for constant in window::Mode::all_constants() {
231-
/// println!("* {}: {} (ord: {})",
233+
/// println!("* window::Mode.{} (original {}) has ordinal value {}.",
232234
/// constant.rust_name(),
233235
/// constant.godot_name(),
234-
/// constant.ord()
236+
/// constant.value().ord()
235237
/// );
236238
/// }
237239
/// ```
@@ -254,6 +256,25 @@ pub trait EngineBitfield: Copy {
254256
fn is_set(self, flag: Self) -> bool {
255257
self.ord() & flag.ord() != 0
256258
}
259+
260+
/// Returns metadata for all bitfield constants.
261+
///
262+
/// This includes all constants as they appear in the bitfield definition.
263+
///
264+
/// Enables introspection of available constants:
265+
/// ```no_run
266+
/// use godot::global::KeyModifierMask;
267+
/// use godot::obj::EngineBitfield;
268+
///
269+
/// for constant in KeyModifierMask::all_constants() {
270+
/// println!("* KeyModifierMask.{} (original {}) has ordinal value {}.",
271+
/// constant.rust_name(),
272+
/// constant.godot_name(),
273+
/// constant.value().ord()
274+
/// );
275+
/// }
276+
/// ```
277+
fn all_constants() -> &'static [EnumConstant<Self>];
257278
}
258279

259280
/// Trait for enums that can be used as indices in arrays.

itest/rust/src/engine_tests/engine_enum_test.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ fn enum_values_duplicates() {
9797
}
9898

9999
assert_eq!(c.value(), value);
100-
assert_eq!(c.ord(), ord);
100+
assert_eq!(c.value().ord(), ord);
101101
}
102102

103103
assert_eq!(InlineAlignment::values(), &expected_distinct_values);
@@ -156,26 +156,38 @@ fn enum_all_constants() {
156156
let first = constants[0];
157157
assert_eq!(first.rust_name(), "TOP_TO");
158158
assert_eq!(first.godot_name(), "INLINE_ALIGNMENT_TOP_TO");
159-
assert_eq!(first.ord(), 0);
160159
assert_eq!(first.value(), InlineAlignment::TOP_TO);
160+
assert_eq!(first.value().ord(), 0);
161161

162162
// Check specific constants at known indices, with equal ordinals.
163163
let known_a = constants[2];
164164
let known_b = constants[11];
165165

166-
assert_eq!(known_a.ord(), 3);
167166
assert_eq!(known_a.rust_name(), "BASELINE_TO");
168167
assert_eq!(known_a.godot_name(), "INLINE_ALIGNMENT_BASELINE_TO");
169168
assert_eq!(known_a.value(), InlineAlignment::BASELINE_TO);
169+
assert_eq!(known_a.value().ord(), 3);
170170

171-
assert_eq!(known_b.ord(), 3);
172171
assert_eq!(known_b.rust_name(), "IMAGE_MASK");
173172
assert_eq!(known_b.godot_name(), "INLINE_ALIGNMENT_IMAGE_MASK");
174173
assert_eq!(known_b.value(), InlineAlignment::IMAGE_MASK);
174+
assert_eq!(known_b.value().ord(), 3);
175175

176176
// "Front-end" values are equal, too.
177177
assert_eq!(
178178
InlineAlignment::IMAGE_MASK.ord(),
179179
InlineAlignment::BASELINE_TO.ord()
180180
);
181181
}
182+
183+
#[itest]
184+
fn bitfield_all_constants() {
185+
let shift_constant = KeyModifierMask::all_constants()
186+
.iter()
187+
.find(|c| c.rust_name() == "SHIFT")
188+
.expect("SHIFT constant should exist");
189+
190+
assert_eq!(shift_constant.godot_name(), "KEY_MASK_SHIFT");
191+
assert_eq!(shift_constant.value(), KeyModifierMask::SHIFT);
192+
assert_eq!(shift_constant.value().ord(), 1 << 25);
193+
}

itest/rust/src/object_tests/enum_test.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::framework::itest;
99
use godot::builtin::varray;
1010
use godot::classes::input::CursorShape;
1111
use godot::classes::mesh::PrimitiveType;
12+
use godot::classes::window::LayoutDirection;
1213
use godot::classes::{time, ArrayMesh};
1314
use godot::global::{Key, Orientation};
1415
use godot::obj::NewGd;
@@ -84,6 +85,9 @@ fn enum_as_str() {
8485
assert_eq!(Key::ESCAPE.as_str(), "ESCAPE");
8586
assert_eq!(Key::TAB.as_str(), "TAB");
8687
assert_eq!(Key::A.as_str(), "A");
88+
89+
#[cfg(since_api = "4.4")] // Deprecated in Godot, LOCALE is now alias for APPLICATION_LOCALE.
90+
assert_eq!(LayoutDirection::LOCALE.as_str(), "APPLICATION_LOCALE");
8791
}
8892

8993
#[itest]

0 commit comments

Comments
 (0)