5
5
*/
6
6
7
7
use crate :: api_parser:: Class ;
8
- use crate :: { ExtensionApi , RustTy , TyName } ;
8
+ use crate :: { util, ExtensionApi , RustTy , TyName } ;
9
+ use proc_macro2:: Ident ;
10
+ use quote:: format_ident;
9
11
use std:: collections:: { HashMap , HashSet } ;
10
12
11
13
#[ derive( Default ) ]
@@ -15,6 +17,8 @@ pub(crate) struct Context<'a> {
15
17
singletons : HashSet < & ' a str > ,
16
18
inheritance_tree : InheritanceTree ,
17
19
cached_rust_types : HashMap < String , RustTy > ,
20
+ notifications_by_class : HashMap < TyName , Vec < ( Ident , i32 ) > > ,
21
+ notification_enum_names_by_class : HashMap < TyName , Ident > ,
18
22
}
19
23
20
24
impl < ' a > Context < ' a > {
@@ -39,15 +43,76 @@ impl<'a> Context<'a> {
39
43
continue ;
40
44
}
41
45
46
+ // Populate class lookup by name
42
47
println ! ( "-- add engine class {}" , class_name. description( ) ) ;
43
48
ctx. engine_classes . insert ( class_name. clone ( ) , class) ;
44
49
50
+ // Populate derived-to-base relations
45
51
if let Some ( base) = class. inherits . as_ref ( ) {
46
52
let base_name = TyName :: from_godot ( base) ;
47
53
println ! ( " -- inherits {}" , base_name. description( ) ) ;
48
- ctx. inheritance_tree . insert ( class_name, base_name) ;
54
+ ctx. inheritance_tree . insert ( class_name. clone ( ) , base_name) ;
55
+ }
56
+
57
+ // Populate notification constants
58
+ if let Some ( constants) = class. constants . as_ref ( ) {
59
+ let mut has_notifications = false ;
60
+
61
+ for constant in constants. iter ( ) {
62
+ if let Some ( rust_constant) = util:: try_to_notification ( & constant) {
63
+ // First time
64
+ if !has_notifications {
65
+ ctx. notifications_by_class
66
+ . insert ( class_name. clone ( ) , Vec :: new ( ) ) ;
67
+ ctx. notification_enum_names_by_class . insert (
68
+ class_name. clone ( ) ,
69
+ make_notification_enum_name ( & class_name) ,
70
+ ) ;
71
+ has_notifications = true ;
72
+ }
73
+
74
+ ctx. notifications_by_class
75
+ . get_mut ( & class_name)
76
+ . expect ( "just inserted constants; must be present" )
77
+ . push ( ( rust_constant, constant. value ) ) ;
78
+ }
79
+ }
49
80
}
50
81
}
82
+
83
+ // Populate remaining notification enum names, by copying the one to nearest base class that has at least 1 notification.
84
+ // At this point all classes with notifications are registered.
85
+ // (Used to avoid re-generating the same notification enum for multiple base classes).
86
+ for class_name in ctx. engine_classes . keys ( ) {
87
+ if ctx
88
+ . notification_enum_names_by_class
89
+ . contains_key ( class_name)
90
+ {
91
+ continue ;
92
+ }
93
+
94
+ let all_bases = ctx. inheritance_tree . collect_all_bases ( class_name) ;
95
+
96
+ let mut nearest = None ;
97
+ for i in 0 ..all_bases. len ( ) {
98
+ if let Some ( nearest_enum_name) =
99
+ ctx. notification_enum_names_by_class . get ( & all_bases[ i] )
100
+ {
101
+ nearest = Some ( ( i, nearest_enum_name. clone ( ) ) ) ;
102
+ break ;
103
+ }
104
+ }
105
+ let ( nearest_index, nearest_enum_name) =
106
+ nearest. expect ( "at least one base must have notifications" ) ;
107
+
108
+ // For all bases inheriting most-derived base that has notification constants, reuse the type name.
109
+ for i in ( 0 ..nearest_index) . rev ( ) {
110
+ let base_name = & all_bases[ i] ;
111
+ ctx. notification_enum_names_by_class
112
+ . insert ( base_name. clone ( ) , nearest_enum_name. clone ( ) ) ;
113
+ }
114
+ }
115
+
51
116
ctx
52
117
}
53
118
@@ -78,6 +143,17 @@ impl<'a> Context<'a> {
78
143
self . cached_rust_types . get ( ty)
79
144
}
80
145
146
+ pub fn notification_constants ( & ' a self , class_name : & TyName ) -> Option < & Vec < ( Ident , i32 ) > > {
147
+ self . notifications_by_class . get ( class_name)
148
+ }
149
+
150
+ pub fn notification_enum_name ( & self , class_name : & TyName ) -> Ident {
151
+ self . notification_enum_names_by_class
152
+ . get ( class_name)
153
+ . expect ( "every class must have a notification enum name" )
154
+ . clone ( )
155
+ }
156
+
81
157
pub fn insert_rust_type ( & mut self , ty : & str , resolved : RustTy ) {
82
158
let prev = self . cached_rust_types . insert ( ty. to_string ( ) , resolved) ;
83
159
assert ! ( prev. is_none( ) , "no overwrites of RustTy" ) ;
@@ -96,6 +172,7 @@ impl InheritanceTree {
96
172
assert ! ( existing. is_none( ) , "Duplicate inheritance insert" ) ;
97
173
}
98
174
175
+ /// Returns all base classes, without the class itself, in order from nearest to furthest (object).
99
176
pub fn collect_all_bases ( & self , derived_name : & TyName ) -> Vec < TyName > {
100
177
let mut maybe_base = derived_name;
101
178
let mut result = vec ! [ ] ;
@@ -107,3 +184,7 @@ impl InheritanceTree {
107
184
result
108
185
}
109
186
}
187
+
188
+ fn make_notification_enum_name ( class_name : & TyName ) -> Ident {
189
+ format_ident ! ( "{}Notification" , class_name. rust_ty)
190
+ }
0 commit comments