Skip to content

Commit 53f6ff9

Browse files
Merge branch 'godot-rust:master' into disconnect-typed-signal
2 parents 05b58bb + 443621c commit 53f6ff9

File tree

7 files changed

+383
-160
lines changed

7 files changed

+383
-160
lines changed

godot-core/src/builtin/color.rs

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ use sys::{ffi_methods, GodotFfi};
2222
///
2323
/// To access its [**HSVA**](ColorHsv) representation, use [`Color::to_hsv`].
2424
///
25+
/// Predefined colors are available as constants, see the corresponding [`impl` block](#impl-Color-1).
26+
///
2527
/// # Godot docs
2628
///
2729
/// [`Color` (stable)](https://docs.godotengine.org/en/stable/classes/class_color.html)
@@ -58,28 +60,29 @@ impl Color {
5860
/// mapped to 1.0.
5961
///
6062
/// _Godot equivalent: the global `Color8` function_
61-
pub fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
63+
pub const fn from_rgba8(r: u8, g: u8, b: u8, a: u8) -> Self {
6264
Self::from_rgba(from_u8(r), from_u8(g), from_u8(b), from_u8(a))
6365
}
6466

65-
/// Constructs a new `Color` with the given components as `u16` words. 0 is mapped to 0.0,
66-
/// 65535 (`0xffff`) is mapped to 1.0.
67-
pub fn from_rgba16(r: u16, g: u16, b: u16, a: u16) -> Self {
67+
/// Constructs a new `Color` with the given components as `u16` words.
68+
///
69+
/// 0 is mapped to 0.0, 65535 (`0xFFFF`) is mapped to 1.0.
70+
pub const fn from_rgba16(r: u16, g: u16, b: u16, a: u16) -> Self {
6871
Self::from_rgba(from_u16(r), from_u16(g), from_u16(b), from_u16(a))
6972
}
7073

7174
/// Constructs a new `Color` from a 32-bits value with the given channel `order`.
7275
///
7376
/// _Godot equivalent: `Color.hex`, if `ColorChannelOrder::Rgba` is used_
74-
pub fn from_u32_rgba(u: u32, order: ColorChannelOrder) -> Self {
77+
pub const fn from_u32_rgba(u: u32, order: ColorChannelOrder) -> Self {
7578
let [r, g, b, a] = order.unpack(u.to_be_bytes());
7679
Color::from_rgba8(r, g, b, a)
7780
}
7881

7982
/// Constructs a new `Color` from a 64-bits value with the given channel `order`.
8083
///
8184
/// _Godot equivalent: `Color.hex64`, if `ColorChannelOrder::Rgba` is used_
82-
pub fn from_u64_rgba(u: u64, order: ColorChannelOrder) -> Self {
85+
pub const fn from_u64_rgba(u: u64, order: ColorChannelOrder) -> Self {
8386
let [r, g, b, a] = order.unpack(to_be_words(u));
8487
Color::from_rgba16(r, g, b, a)
8588
}
@@ -383,7 +386,7 @@ pub enum ColorChannelOrder {
383386
}
384387

385388
impl ColorChannelOrder {
386-
fn pack<T>(self, rgba: [T; 4]) -> [T; 4] {
389+
const fn pack<T: Copy>(self, rgba: [T; 4]) -> [T; 4] {
387390
let [r, g, b, a] = rgba;
388391
match self {
389392
ColorChannelOrder::RGBA => [r, g, b, a],
@@ -392,7 +395,7 @@ impl ColorChannelOrder {
392395
}
393396
}
394397

395-
fn unpack<T>(self, xyzw: [T; 4]) -> [T; 4] {
398+
const fn unpack<T: Copy>(self, xyzw: [T; 4]) -> [T; 4] {
396399
let [x, y, z, w] = xyzw;
397400
match self {
398401
ColorChannelOrder::RGBA => [x, y, z, w],
@@ -510,12 +513,12 @@ impl ops::Neg for Color {
510513
}
511514

512515
/// Converts a single channel byte to a float in the range 0 to 1.
513-
fn from_u8(byte: u8) -> f32 {
516+
const fn from_u8(byte: u8) -> f32 {
514517
byte as f32 / 255.0
515518
}
516519

517520
/// Converts a single channel `u16` word to a float in the range 0 to 1.
518-
fn from_u16(byte: u16) -> f32 {
521+
const fn from_u16(byte: u16) -> f32 {
519522
byte as f32 / 65535.0
520523
}
521524

@@ -542,7 +545,7 @@ fn from_be_words(words: [u16; 4]) -> u64 {
542545
}
543546

544547
/// Unpacks a `u64` into four `u16` words in big-endian order.
545-
fn to_be_words(mut u: u64) -> [u16; 4] {
548+
const fn to_be_words(mut u: u64) -> [u16; 4] {
546549
let w = (u & 0xffff) as u16;
547550
u >>= 16;
548551
let z = (u & 0xffff) as u16;

godot-core/src/builtin/color_constants.rs

Lines changed: 314 additions & 148 deletions
Large diffs are not rendered by default.

godot-macros/src/class/data_models/interface_trait_impl.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,8 +227,12 @@ fn handle_init<'a>(
227227
godot_init_impl, ..
228228
} = decls;
229229

230+
// If #[class(init)] or #[class(no_init)] is provided, deny overriding manual init().
231+
let deny_manual_init_macro = util::format_class_deny_manual_init_macro(class_name);
232+
230233
*godot_init_impl = quote! {
231234
#godot_init_impl
235+
#deny_manual_init_macro!();
232236

233237
#(#cfg_attrs)*
234238
impl ::godot::obj::cap::GodotDefault for #class_name {

godot-macros/src/class/derive_godot_class.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
148148
// Note: one limitation is that macros don't work for `impl nested::MyClass` blocks.
149149
let visibility_macro = make_visibility_macro(class_name, class.vis_marker.as_ref());
150150
let base_field_macro = make_base_field_macro(class_name, fields.base_field.is_some());
151+
let deny_manual_init_macro = make_deny_manual_init_macro(class_name, struct_cfg.init_strategy);
151152

152153
Ok(quote! {
153154
impl ::godot::obj::GodotClass for #class_name {
@@ -180,6 +181,7 @@ pub fn derive_godot_class(item: venial::Item) -> ParseResult<TokenStream> {
180181
#init_expecter
181182
#visibility_macro
182183
#base_field_macro
184+
#deny_manual_init_macro
183185
#( #deprecations )*
184186
#( #errors )*
185187

@@ -236,6 +238,35 @@ fn make_base_field_macro(class_name: &Ident, has_base_field: bool) -> TokenStrea
236238
}
237239
}
238240

241+
/// Generates code for a decl-macro that prevents manual `init()` for incompatible init strategies.
242+
fn make_deny_manual_init_macro(class_name: &Ident, init_strategy: InitStrategy) -> TokenStream {
243+
let macro_name = util::format_class_deny_manual_init_macro(class_name);
244+
245+
let class_attr = match init_strategy {
246+
InitStrategy::Absent => "#[class(no_init)]",
247+
InitStrategy::Generated => "#[class(init)]",
248+
InitStrategy::UserDefined => {
249+
// For classes that expect manual init, do nothing.
250+
return quote! {
251+
macro_rules! #macro_name {
252+
() => {};
253+
}
254+
};
255+
}
256+
};
257+
258+
let error_message =
259+
format!("Class `{class_name}` is marked with {class_attr} but provides an init() method.");
260+
261+
quote! {
262+
macro_rules! #macro_name {
263+
() => {
264+
compile_error!(#error_message);
265+
};
266+
}
267+
}
268+
}
269+
239270
/// Checks at compile time that a function with the given name exists on `Self`.
240271
#[must_use]
241272
pub fn make_existence_check(ident: &Ident) -> TokenStream {

godot-macros/src/util/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,8 @@ pub fn format_class_visibility_macro(class_name: &Ident) -> Ident {
418418
pub fn format_class_base_field_macro(class_name: &Ident) -> Ident {
419419
format_ident!("__godot_{class_name}_has_base_field_macro")
420420
}
421+
422+
/// Returns the name of the macro used to deny manual `init()` for incompatible init strategies.
423+
pub fn format_class_deny_manual_init_macro(class_name: &Ident) -> Ident {
424+
format_ident!("__deny_manual_init_{class_name}")
425+
}

itest/rust/src/builtin_tests/color_test.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,3 +198,17 @@ fn color_hsv_multi_roundtrip() {
198198
assert_eq_approx!(original, c_back);
199199
}
200200
}
201+
202+
// Check that color constants match their Godot value exactly.
203+
//
204+
// Occasionally, this can be manually cross-checked against extension_api.json. We currently don't codegen those constants, and the values
205+
// there are in float, so may not match exactly.
206+
#[itest]
207+
fn color_constants() {
208+
for (name, rust_color) in Color::ALL_GODOT_COLORS.iter().copied() {
209+
let godot_color = Color::from_string(name)
210+
.unwrap_or_else(|| panic!("Color constant {name} not found in Godot"));
211+
212+
assert_eq!(rust_color, godot_color, "Color mismatch for {name}");
213+
}
214+
}

itest/rust/src/engine_tests/codegen_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ impl CodegenTest2 {
142142
macro_rules! make_class {
143143
($ClassName:ident, $BaseName:ident) => {
144144
#[derive(GodotClass)]
145-
#[class(no_init, base=$BaseName)]
145+
#[class(base=$BaseName)]
146146
pub struct $ClassName {
147147
base: Base<godot::classes::$BaseName>,
148148
}

0 commit comments

Comments
 (0)