@@ -43,7 +43,7 @@ pub(crate) fn generate_class_files(
43
43
}
44
44
45
45
let generated_class = make_class ( class, & class_name, ctx) ;
46
- let file_contents = generated_class. tokens . to_string ( ) ;
46
+ let file_contents = generated_class. code . to_string ( ) ;
47
47
48
48
let out_path = gen_path. join ( format ! ( "{}.rs" , module_name. rust_mod) ) ;
49
49
std:: fs:: write ( & out_path, file_contents) . expect ( "failed to write class file" ) ;
@@ -56,7 +56,7 @@ pub(crate) fn generate_class_files(
56
56
. has_own_notification_enum
57
57
. then_some ( generated_class. notification_enum_name ) ,
58
58
inherits_macro_ident : generated_class. inherits_macro_ident ,
59
- is_pub : generated_class. has_pub_module ,
59
+ is_pub_sidecar : generated_class. has_sidecar_module ,
60
60
} ) ;
61
61
}
62
62
@@ -94,7 +94,7 @@ pub(crate) fn generate_builtin_class_files(
94
94
95
95
let generated_class =
96
96
make_builtin_class ( class, & class_name, & inner_class_name, type_info, ctx) ;
97
- let file_contents = generated_class. tokens . to_string ( ) ;
97
+ let file_contents = generated_class. code . to_string ( ) ;
98
98
99
99
let out_path = gen_path. join ( format ! ( "{}.rs" , module_name. rust_mod) ) ;
100
100
std:: fs:: write ( & out_path, file_contents) . expect ( "failed to write class file" ) ;
@@ -112,13 +112,95 @@ pub(crate) fn generate_builtin_class_files(
112
112
out_files. push ( out_path) ;
113
113
}
114
114
115
+ fn make_class_doc (
116
+ class_name : & TyName ,
117
+ base_ident_opt : Option < Ident > ,
118
+ has_notification_enum : bool ,
119
+ has_sidecar_module : bool ,
120
+ ) -> String {
121
+ let TyName { rust_ty, godot_ty } = class_name;
122
+
123
+ let inherits_line = if let Some ( base) = base_ident_opt {
124
+ format ! ( "Inherits [`{base}`][crate::engine::{base}]." )
125
+ } else {
126
+ "This is the base class for all other classes at the root of the hierarchy. \
127
+ Every instance of `Object` can be stored in a [`Gd`][crate::obj::Gd] smart pointer."
128
+ . to_string ( )
129
+ } ;
130
+
131
+ let notify_line = if has_notification_enum {
132
+ format ! ( "* [`{rust_ty}Notification`][crate::engine::notify::{rust_ty}Notification]: notification type\n " )
133
+ } else {
134
+ String :: new ( )
135
+ } ;
136
+
137
+ let sidecar_line = if has_sidecar_module {
138
+ let module_name = ModName :: from_godot ( & class_name. godot_ty ) . rust_mod ;
139
+ format ! ( "* [`{module_name}`][crate::engine::{module_name}]: sidecar module with related enum/flag types\n " )
140
+ } else {
141
+ String :: new ( )
142
+ } ;
143
+
144
+ let online_link = format ! (
145
+ "https://docs.godotengine.org/en/stable/classes/class_{}.html" ,
146
+ godot_ty. to_ascii_lowercase( )
147
+ ) ;
148
+
149
+ format ! (
150
+ "Godot class `{godot_ty}.`\n \n \
151
+ \
152
+ {inherits_line}\n \n \
153
+ \
154
+ Related symbols:\n \n \
155
+ {sidecar_line}\
156
+ * [`{rust_ty}Virtual`][crate::engine::{rust_ty}Virtual]: virtual methods\n \
157
+ {notify_line}\
158
+ \n \n \
159
+ See also [Godot docs for `{godot_ty}`]({online_link}).\n \n ",
160
+ )
161
+ }
162
+
163
+ fn make_virtual_trait_doc ( class_name : & TyName ) -> String {
164
+ let TyName { rust_ty, godot_ty } = class_name;
165
+
166
+ let online_link = format ! (
167
+ "https://docs.godotengine.org/en/stable/classes/class_{}.html#methods" ,
168
+ godot_ty. to_ascii_lowercase( )
169
+ ) ;
170
+
171
+ format ! (
172
+ "Virtual methods for class [`{rust_ty}`][crate::engine::{rust_ty}].\
173
+ \n \n \
174
+ These methods represent constructors (`init`) or callbacks invoked by the engine.\
175
+ \n \n \
176
+ See also [Godot docs for `{godot_ty}` methods]({online_link}).\n \n "
177
+ )
178
+ }
179
+
180
+ fn make_module_doc ( class_name : & TyName ) -> String {
181
+ let TyName { rust_ty, godot_ty } = class_name;
182
+
183
+ let online_link = format ! (
184
+ "https://docs.godotengine.org/en/stable/classes/class_{}.html#enumerations" ,
185
+ godot_ty. to_ascii_lowercase( )
186
+ ) ;
187
+
188
+ format ! (
189
+ "Sidecar module for class [`{rust_ty}`][crate::engine::{rust_ty}].\
190
+ \n \n \
191
+ Defines related flag and enum types. In GDScript, those are nested under the class scope.\
192
+ \n \n \
193
+ See also [Godot docs for `{godot_ty}` enums]({online_link}).\n \n "
194
+ )
195
+ }
196
+
115
197
fn make_constructor ( class : & Class , ctx : & Context ) -> TokenStream {
116
198
let godot_class_name = & class. name ;
117
199
if ctx. is_singleton ( godot_class_name) {
118
200
// Note: we cannot return &'static mut Self, as this would be very easy to mutably alias.
119
- // &'static Self would be possible, but we would lose the whole mutability information (even if that
120
- // is best-effort and not strict Rust mutability, it makes the API much more usable).
121
- // As long as the user has multiple Gd smart pointers to the same singletons, only the internal raw pointers.
201
+ // &'static Self would be possible, but we would lose the whole mutability information (even if that is best-effort and
202
+ // not strict Rust mutability, it makes the API much more usable).
203
+ // As long as the user has multiple Gd smart pointers to the same singletons, only the internal raw pointers are aliased .
122
204
// See also Deref/DerefMut impl for Gd.
123
205
quote ! {
124
206
pub fn singleton( ) -> Gd <Self > {
@@ -165,12 +247,12 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
165
247
let virtual_trait_str = class_name. virtual_trait_name ( ) ;
166
248
167
249
// Idents and tokens
168
- let base = match class. inherits . as_ref ( ) {
250
+ let ( base_ty , base_ident_opt ) = match class. inherits . as_ref ( ) {
169
251
Some ( base) => {
170
252
let base = ident ( & to_pascal_case ( base) ) ;
171
- quote ! { crate :: engine:: #base }
253
+ ( quote ! { crate :: engine:: #base } , Some ( base ) )
172
254
}
173
- None => quote ! { ( ) } ,
255
+ None => ( quote ! { ( ) } , None ) ,
174
256
} ;
175
257
176
258
let constructor = make_constructor ( class, ctx) ;
@@ -181,8 +263,17 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
181
263
let all_bases = ctx. inheritance_tree ( ) . collect_all_bases ( class_name) ;
182
264
let ( notification_enum, notification_enum_name) =
183
265
make_notification_enum ( class_name, & all_bases, ctx) ;
266
+ let has_sidecar_module = !enums. is_empty ( ) ;
267
+ let class_doc = make_class_doc (
268
+ class_name,
269
+ base_ident_opt,
270
+ notification_enum. is_some ( ) ,
271
+ has_sidecar_module,
272
+ ) ;
273
+ let module_doc = make_module_doc ( class_name) ;
184
274
let virtual_trait = make_virtual_methods_trait (
185
275
class,
276
+ class_name,
186
277
& all_bases,
187
278
& virtual_trait_str,
188
279
& notification_enum_name,
@@ -200,6 +291,8 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
200
291
201
292
// mod re_export needed, because class should not appear inside the file module, and we can't re-export private struct as pub
202
293
let tokens = quote ! {
294
+ #![ doc = #module_doc]
295
+
203
296
use godot_ffi as sys;
204
297
use crate :: engine:: * ;
205
298
use crate :: engine:: notify:: * ;
@@ -210,6 +303,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
210
303
pub ( super ) mod re_export {
211
304
use super :: * ;
212
305
306
+ #[ doc = #class_doc]
213
307
#[ derive( Debug ) ]
214
308
#[ repr( transparent) ]
215
309
pub struct #class_name {
@@ -224,7 +318,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
224
318
#constants
225
319
}
226
320
impl crate :: obj:: GodotClass for #class_name {
227
- type Base = #base ;
321
+ type Base = #base_ty ;
228
322
type Declarer = crate :: obj:: dom:: EngineDomain ;
229
323
type Mem = crate :: obj:: mem:: #memory;
230
324
@@ -242,7 +336,7 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
242
336
impl crate :: obj:: Inherits <crate :: engine:: #all_bases> for #class_name { }
243
337
) *
244
338
impl std:: ops:: Deref for #class_name {
245
- type Target = #base ;
339
+ type Target = #base_ty ;
246
340
247
341
fn deref( & self ) -> & Self :: Target {
248
342
// SAFETY: same assumptions as `impl Deref for Gd<T>`, see there for comments
@@ -273,30 +367,37 @@ fn make_class(class: &Class, class_name: &TyName, ctx: &mut Context) -> Generate
273
367
// note: TypePtr -> ObjectPtr conversion OK?
274
368
275
369
GeneratedClass {
276
- tokens,
370
+ code : tokens,
277
371
notification_enum_name,
278
372
has_own_notification_enum : notification_enum. is_some ( ) ,
279
373
inherits_macro_ident : inherits_macro,
280
- has_pub_module : !enums . is_empty ( ) ,
374
+ has_sidecar_module ,
281
375
}
282
376
}
283
377
284
378
fn make_notify_method ( class_name : & TyName , ctx : & mut Context ) -> TokenStream {
285
379
let enum_name = ctx. notification_enum_name ( class_name) ;
286
380
287
381
quote ! {
288
- /// Sends the `notification` to all classes inherited by the object, triggering calls to `on_notification()`.
382
+ /// ⚠️ Sends a Godot notification to all classes inherited by the object.
383
+ ///
384
+ /// Triggers calls to `on_notification()`, and depending on the notification, also to Godot's lifecycle callbacks such as `ready()`.
289
385
///
290
386
/// Starts from the highest ancestor (the `Object` class) and goes down the hierarchy.
387
+ /// See also [Godot docs for `Object::notification()`](https://docs.godotengine.org/en/latest/classes/class_object.html#id3).
388
+ ///
389
+ /// # Panics
291
390
///
292
- /// See [docs for `Object::notification()`](https://docs.godotengine.org/en/latest/classes/class_object.html#id3) in Godot.
391
+ /// If you call this method on a user-defined object while holding a `GdRef` or `GdMut` guard on the instance, you will encounter
392
+ /// a panic. The reason is that the receiving virtual method `on_notification()` acquires a `GdMut` lock dynamically, which must
393
+ /// be exclusive.
293
394
pub fn issue_notification( & mut self , what: #enum_name) {
294
395
self . notification( i32 :: from( what) as i64 , false ) ;
295
396
}
296
397
297
- /// Like [`Self::issue_notification()`], but starts at the most-derived class and goes up the hierarchy.
398
+ /// ⚠️ Like [`Self::issue_notification()`], but starts at the most-derived class and goes up the hierarchy.
298
399
///
299
- /// See [ docs for `Object::notification()`](https://docs.godotengine.org/en/latest/classes/class_object.html#id3) in Godot .
400
+ /// See docs of that method, including the panics .
300
401
pub fn issue_notification_reversed( & mut self , what: #enum_name) {
301
402
self . notification( i32 :: from( what) as i64 , true ) ;
302
403
}
@@ -449,7 +550,7 @@ fn make_builtin_class(
449
550
} ;
450
551
// note: TypePtr -> ObjectPtr conversion OK?
451
552
452
- GeneratedBuiltin { tokens }
553
+ GeneratedBuiltin { code : tokens }
453
554
}
454
555
455
556
fn make_module_file ( classes_and_modules : Vec < GeneratedClassModule > ) -> TokenStream {
@@ -461,7 +562,7 @@ fn make_module_file(classes_and_modules: Vec<GeneratedClassModule>) -> TokenStre
461
562
module_name,
462
563
class_name,
463
564
own_notification_enum_name,
464
- is_pub,
565
+ is_pub_sidecar : is_pub,
465
566
..
466
567
} = m;
467
568
let virtual_trait_name = ident ( & class_name. virtual_trait_name ( ) ) ;
@@ -1136,59 +1237,67 @@ fn make_return(
1136
1237
1137
1238
fn make_virtual_methods_trait (
1138
1239
class : & Class ,
1139
- all_bases : & [ TyName ] ,
1240
+ class_name : & TyName ,
1241
+ all_base_names : & [ TyName ] ,
1140
1242
trait_name : & str ,
1141
1243
notification_enum_name : & Ident ,
1142
1244
ctx : & mut Context ,
1143
1245
) -> TokenStream {
1144
1246
let trait_name = ident ( trait_name) ;
1145
1247
1146
- let virtual_method_fns = make_all_virtual_methods ( class, all_bases , ctx) ;
1248
+ let virtual_method_fns = make_all_virtual_methods ( class, all_base_names , ctx) ;
1147
1249
let special_virtual_methods = special_virtual_methods ( notification_enum_name) ;
1148
1250
1251
+ let trait_doc = make_virtual_trait_doc ( class_name) ;
1252
+
1149
1253
quote ! {
1254
+ #[ doc = #trait_doc]
1150
1255
#[ allow( unused_variables) ]
1151
1256
#[ allow( clippy:: unimplemented) ]
1152
- pub trait #trait_name: crate :: private:: You_forgot_the_attribute__godot_api + crate :: obj:: GodotClass {
1153
- #( #virtual_method_fns ) *
1257
+ pub trait #trait_name: crate :: obj:: GodotClass + crate :: private:: You_forgot_the_attribute__godot_api {
1154
1258
#special_virtual_methods
1259
+ #( #virtual_method_fns ) *
1155
1260
}
1156
1261
}
1157
1262
}
1158
1263
1159
1264
fn special_virtual_methods ( notification_enum_name : & Ident ) -> TokenStream {
1160
1265
quote ! {
1266
+ #[ doc( hidden) ]
1161
1267
fn register_class( builder: & mut crate :: builder:: ClassBuilder <Self >) {
1162
1268
unimplemented!( )
1163
1269
}
1164
1270
1165
1271
/// Godot constructor, accepting an injected `base` object.
1166
1272
///
1167
1273
/// `base` refers to the base instance of the class, which can either be stored in a `#[base]` field or discarded.
1168
- /// This method returns a fully-constructed instance, which will then be moved into a `Gd<T>` pointer.
1274
+ /// This method returns a fully-constructed instance, which will then be moved into a [`Gd<T>`][crate::obj::Gd] pointer.
1275
+ ///
1276
+ /// If the class has a `#[class(init)]` attribute, this method will be auto-generated and must not be overridden.
1169
1277
fn init( base: crate :: obj:: Base <Self :: Base >) -> Self {
1170
1278
unimplemented!( )
1171
1279
}
1172
1280
1173
1281
/// String representation of the Godot instance.
1174
1282
///
1175
1283
/// Override this method to define how the instance is represented as a string.
1176
- /// Used by `str()` and `print()` in GDScript, among others .
1284
+ /// Used by `impl Display for Gd<T>`, as well as ` str()` and `print()` in GDScript.
1177
1285
fn to_string( & self ) -> crate :: builtin:: GodotString {
1178
1286
unimplemented!( )
1179
1287
}
1180
1288
1181
1289
/// Called when the object receives a Godot notification.
1182
1290
///
1183
- /// The type of notification can be identified through `what`, by comparing it with a `NOTIFICATION_*` constant. These constants are
1184
- /// defined across multiple classes, most notably in [`Node`](https://docs.godotengine.org/en/stable/classes/class_node.html#constants).
1291
+ /// The type of notification can be identified through `what`. The enum is designed to hold all possible `NOTIFICATION_*`
1292
+ /// constants that the current class can handle. However, this is not validated in Godot, so an enum variant `Unknown` exists
1293
+ /// to represent integers out of known constants (mistakes or future additions).
1185
1294
///
1186
- /// This method is named `_notification` in Godot, but `on_notification` in Rust, to avoid conflicts with the
1187
- /// [`Object::notification `][crate::engine::Object::notification ] method that _issues_ notifications .
1295
+ /// This method is named `_notification` in Godot, but `on_notification` in Rust. To _send_ notifications, use the
1296
+ /// [`Object::issue_notification `][crate::engine::Object::issue_notification ] method.
1188
1297
///
1189
1298
/// See also in Godot docs:
1190
1299
/// * [`Object::_notification`](https://docs.godotengine.org/en/stable/classes/class_object.html#class-object-method-notification).
1191
- /// * [Godot notifications ](https://docs.godotengine.org/en/stable/tutorials/best_practices/godot_notifications.html).
1300
+ /// * [Notifications tutorial ](https://docs.godotengine.org/en/stable/tutorials/best_practices/godot_notifications.html).
1192
1301
fn on_notification( & mut self , what: #notification_enum_name) {
1193
1302
unimplemented!( )
1194
1303
}
@@ -1229,7 +1338,7 @@ fn make_virtual_method(class_method: &ClassMethod, ctx: &mut Context) -> TokenSt
1229
1338
1230
1339
fn make_all_virtual_methods (
1231
1340
class : & Class ,
1232
- all_bases : & [ TyName ] ,
1341
+ all_base_names : & [ TyName ] ,
1233
1342
ctx : & mut Context ,
1234
1343
) -> Vec < TokenStream > {
1235
1344
let mut all_virtuals = vec ! [ ] ;
@@ -1245,7 +1354,7 @@ fn make_all_virtual_methods(
1245
1354
// Get virtuals defined on the current class.
1246
1355
extend_virtuals ( class) ;
1247
1356
// Add virtuals from superclasses.
1248
- for base in all_bases {
1357
+ for base in all_base_names {
1249
1358
let superclass = ctx. get_engine_class ( base) ;
1250
1359
extend_virtuals ( superclass) ;
1251
1360
}
0 commit comments