1use std::collections::{BTreeMap, HashMap, HashSet};
2
3use crate::config::CodegenConfig;
4use crate::schema::{
5 ClassInfo, EnumInfo, FUNC_BLUEPRINT_EVENT, FUNC_NATIVE, FUNC_STATIC, FunctionInfo,
6 PropertyInfo, StructInfo,
7};
8
9pub struct CodegenContext {
10 pub classes: HashMap<String, ClassInfo>,
11 pub structs: HashMap<String, StructInfo>,
12 pub enums: HashMap<String, EnumInfo>,
13
14 pub package_to_module: HashMap<String, String>,
15 pub module_to_feature: HashMap<String, String>,
16 pub enabled_modules: HashSet<String>,
17
18 pub module_classes: BTreeMap<String, Vec<ClassInfo>>,
19 pub module_structs: BTreeMap<String, Vec<StructInfo>>,
20 pub module_enums: BTreeMap<String, Vec<EnumInfo>>,
21
22 pub func_table: Vec<FuncEntry>,
23
24 pub module_deps: BTreeMap<String, std::collections::BTreeSet<String>>,
25 pub cpp_prefix: String,
26}
27
28#[derive(Clone)]
29pub struct FuncEntry {
30 pub func_id: u32,
31 pub module_name: String,
32 pub class_name: String,
33 pub func_name: String,
34 pub rust_func_name: String,
35 pub func: FunctionInfo,
36 pub cpp_class_name: String,
37 pub header: String,
38}
39
40impl CodegenContext {
41 pub fn new(
42 classes: Vec<ClassInfo>,
43 structs: Vec<StructInfo>,
44 enums: Vec<EnumInfo>,
45 config: &CodegenConfig,
46 ) -> Self {
47 let mut package_to_module = HashMap::new();
48 let mut module_to_feature_map = HashMap::new();
49
50 for (pkg, mapping) in &config.modules {
51 package_to_module.insert(pkg.clone(), mapping.module.clone());
52 module_to_feature_map.insert(mapping.module.clone(), mapping.feature.clone());
53 }
54
55 let enabled_features: HashSet<&str> = config.features.iter().map(|s| s.as_str()).collect();
56 let mut enabled_modules = HashSet::new();
57
58 for (pkg, mapping) in &config.modules {
59 if enabled_features.contains(mapping.feature.as_str()) {
60 enabled_modules.insert(mapping.module.clone());
61
62 package_to_module
63 .entry(pkg.clone())
64 .or_insert_with(|| mapping.module.clone());
65 }
66 }
67
68 let mut all_packages = HashSet::new();
69
70 for c in &classes {
71 all_packages.insert(c.package.clone());
72 }
73
74 for s in &structs {
75 all_packages.insert(s.package.clone());
76 }
77
78 for e in &enums {
79 all_packages.insert(e.package.clone());
80 }
81
82 for pkg in &all_packages {
83 if !package_to_module.contains_key(pkg) {
84 let module = crate::naming::to_snake_case(pkg);
85 package_to_module.insert(pkg.clone(), module);
86 }
87 }
88
89 let mut module_classes: BTreeMap<String, Vec<ClassInfo>> = BTreeMap::new();
90 let mut module_structs: BTreeMap<String, Vec<StructInfo>> = BTreeMap::new();
91 let mut module_enums: BTreeMap<String, Vec<EnumInfo>> = BTreeMap::new();
92
93 let mut classes_map = HashMap::new();
94
95 for c in classes {
96 if let Some(module) = package_to_module.get(&c.package)
97 && enabled_modules.contains(module)
98 {
99 classes_map.insert(c.name.clone(), c.clone());
100 module_classes.entry(module.clone()).or_default().push(c);
101 }
102 }
103
104 let mut structs_map = HashMap::new();
105
106 for s in structs {
107 if let Some(module) = package_to_module.get(&s.package)
108 && enabled_modules.contains(module)
109 {
110 structs_map.insert(s.name.clone(), s.clone());
111 module_structs.entry(module.clone()).or_default().push(s);
112 }
113 }
114
115 let mut enums_map = HashMap::new();
116
117 for e in enums {
118 if let Some(module) = package_to_module.get(&e.package)
119 && enabled_modules.contains(module)
120 {
121 enums_map.insert(e.name.clone(), e.clone());
122 module_enums.entry(module.clone()).or_default().push(e);
123 }
124 }
125
126 for classes in module_classes.values_mut() {
127 classes.sort_by(|a, b| a.name.cmp(&b.name));
128 }
129
130 for structs in module_structs.values_mut() {
131 structs.sort_by(|a, b| a.name.cmp(&b.name));
132 }
133
134 for enums in module_enums.values_mut() {
135 enums.sort_by(|a, b| a.name.cmp(&b.name));
136 }
137
138 add_interface_functions(&mut module_classes, &mut classes_map);
139
140 let mut ctx = CodegenContext {
141 classes: classes_map,
142 structs: structs_map,
143 enums: enums_map,
144 package_to_module,
145 module_to_feature: module_to_feature_map,
146 enabled_modules,
147 module_classes,
148 module_structs,
149 module_enums,
150 func_table: Vec::new(),
151 module_deps: BTreeMap::new(),
152 cpp_prefix: String::new(),
153 };
154
155 ctx.module_deps = ctx.compute_module_deps();
156
157 ctx
158 }
159
160 fn compute_module_deps(&self) -> BTreeMap<String, std::collections::BTreeSet<String>> {
161 let mut deps: BTreeMap<String, std::collections::BTreeSet<String>> = BTreeMap::new();
162
163 for m in &self.enabled_modules {
164 deps.entry(m.clone()).or_default();
165 }
166
167 for (module, classes) in &self.module_classes {
168 for class in classes {
169 if let Some(parent) = &class.super_class
170 && let Some(pc) = self.classes.get(parent.as_str())
171 {
172 self.record_dep(module, &pc.package, &mut deps);
173 }
174
175 for iface in &class.interfaces {
176 if let Some(ic) = self.classes.get(interface_class_name(iface)) {
177 self.record_dep(module, &ic.package, &mut deps);
178 }
179 }
180
181 for prop in &class.props {
182 self.walk_prop_deps(prop, module, &mut deps);
183 }
184
185 for func in &class.funcs {
186 for p in &func.params {
187 self.walk_param_deps(p, module, &mut deps);
188 }
189 }
190 }
191 }
192
193 for (module, structs) in &self.module_structs {
194 for s in structs {
195 if let Some(parent) = &s.super_struct
196 && let Some(ps) = self.structs.get(parent.as_str())
197 {
198 self.record_dep(module, &ps.package, &mut deps);
199 }
200
201 for prop in &s.props {
202 self.walk_prop_deps(prop, module, &mut deps);
203 }
204 }
205 }
206
207 deps
208 }
209
210 fn record_dep(
211 &self,
212 current_module: &str,
213 target_package: &str,
214 deps: &mut BTreeMap<String, std::collections::BTreeSet<String>>,
215 ) {
216 if let Some(target_module) = self.package_to_module.get(target_package)
217 && target_module != current_module
218 && self.enabled_modules.contains(target_module)
219 {
220 deps.entry(current_module.to_string())
221 .or_default()
222 .insert(target_module.clone());
223 }
224 }
225
226 fn walk_prop_deps(
227 &self,
228 prop: &PropertyInfo,
229 current_module: &str,
230 deps: &mut BTreeMap<String, std::collections::BTreeSet<String>>,
231 ) {
232 if let Some(en) = &prop.enum_name
233 && let Some(e) = self.enums.get(en.as_str())
234 {
235 self.record_dep(current_module, &e.package, deps);
236 }
237
238 if let Some(cn) = &prop.class_name
239 && let Some(c) = self.classes.get(cn.as_str())
240 {
241 self.record_dep(current_module, &c.package, deps);
242 }
243
244 if let Some(mc) = &prop.meta_class_name
245 && let Some(c) = self.classes.get(mc.as_str())
246 {
247 self.record_dep(current_module, &c.package, deps);
248 }
249
250 if let Some(sn) = &prop.struct_name
251 && let Some(s) = self.structs.get(sn.as_str())
252 {
253 self.record_dep(current_module, &s.package, deps);
254 }
255
256 if let Some(in_) = &prop.interface_name
257 && let Some(c) = self.classes.get(in_.as_str())
258 {
259 self.record_dep(current_module, &c.package, deps);
260 }
261
262 if let Some(fi) = &prop.func_info {
263 self.walk_delegate_func_info_deps(fi, current_module, deps);
264 }
265
266 if let Some(inner) = &prop.inner_prop {
267 self.walk_prop_deps(inner, current_module, deps);
268 }
269
270 if let Some(key) = &prop.key_prop {
271 self.walk_prop_deps(key, current_module, deps);
272 }
273
274 if let Some(value) = &prop.value_prop {
275 self.walk_prop_deps(value, current_module, deps);
276 }
277
278 if let Some(element) = &prop.element_prop {
279 self.walk_prop_deps(element, current_module, deps);
280 }
281 }
282
283 fn walk_param_deps(
284 &self,
285 param: &crate::schema::ParamInfo,
286 current_module: &str,
287 deps: &mut BTreeMap<String, std::collections::BTreeSet<String>>,
288 ) {
289 if let Some(en) = ¶m.enum_name
290 && let Some(e) = self.enums.get(en.as_str())
291 {
292 self.record_dep(current_module, &e.package, deps);
293 }
294
295 if let Some(cn) = ¶m.class_name
296 && let Some(c) = self.classes.get(cn.as_str())
297 {
298 self.record_dep(current_module, &c.package, deps);
299 }
300
301 if let Some(mc) = ¶m.meta_class_name
302 && let Some(c) = self.classes.get(mc.as_str())
303 {
304 self.record_dep(current_module, &c.package, deps);
305 }
306
307 if let Some(sn) = ¶m.struct_name
308 && let Some(s) = self.structs.get(sn.as_str())
309 {
310 self.record_dep(current_module, &s.package, deps);
311 }
312
313 if let Some(in_) = ¶m.interface_name
314 && let Some(c) = self.classes.get(in_.as_str())
315 {
316 self.record_dep(current_module, &c.package, deps);
317 }
318
319 if let Some(inner) = ¶m.inner_prop {
320 self.walk_prop_deps(inner, current_module, deps);
321 }
322
323 if let Some(key) = ¶m.key_prop {
324 self.walk_prop_deps(key, current_module, deps);
325 }
326
327 if let Some(value) = ¶m.value_prop {
328 self.walk_prop_deps(value, current_module, deps);
329 }
330
331 if let Some(element) = ¶m.element_prop {
332 self.walk_prop_deps(element, current_module, deps);
333 }
334 }
335
336 fn walk_delegate_func_info_deps(
337 &self,
338 fi: &serde_json::Value,
339 current_module: &str,
340 deps: &mut BTreeMap<String, std::collections::BTreeSet<String>>,
341 ) {
342 let Some(params) = fi.get("params").and_then(|p| p.as_array()) else {
343 return;
344 };
345
346 for p in params {
347 if let Some(en) = p.get("enum_name").and_then(|v| v.as_str())
348 && let Some(e) = self.enums.get(en)
349 {
350 self.record_dep(current_module, &e.package, deps);
351 }
352
353 if let Some(cn) = p.get("class_name").and_then(|v| v.as_str())
354 && let Some(c) = self.classes.get(cn)
355 {
356 self.record_dep(current_module, &c.package, deps);
357 }
358
359 if let Some(mc) = p.get("meta_class_name").and_then(|v| v.as_str())
360 && let Some(c) = self.classes.get(mc)
361 {
362 self.record_dep(current_module, &c.package, deps);
363 }
364
365 if let Some(sn) = p.get("struct_name").and_then(|v| v.as_str())
366 && let Some(s) = self.structs.get(sn)
367 {
368 self.record_dep(current_module, &s.package, deps);
369 }
370
371 if let Some(in_) = p.get("interface_name").and_then(|v| v.as_str())
372 && let Some(c) = self.classes.get(in_)
373 {
374 self.record_dep(current_module, &c.package, deps);
375 }
376 }
377 }
378
379 #[allow(dead_code)]
380 pub fn is_type_available(&self, type_name: &str) -> bool {
381 self.classes.contains_key(type_name)
382 || self.structs.contains_key(type_name)
383 || self.enums.contains_key(type_name)
384 }
385
386 #[allow(dead_code)]
387 pub fn module_for_package(&self, package: &str) -> Option<&str> {
388 self.package_to_module.get(package).map(|s| s.as_str())
389 }
390
391 pub fn feature_for_module(&self, module: &str) -> Option<&str> {
392 self.module_to_feature.get(module).map(|s| s.as_str())
393 }
394
395 pub fn enum_actual_repr(&self, enum_name: &str) -> Option<&'static str> {
396 let e = self.enums.get(enum_name)?;
397 let has_negative = e.pairs.iter().any(|(_, v)| *v < 0);
398
399 Some(if has_negative {
400 match e.underlying_type.as_str() {
401 "uint8" => "i8",
402 "int8" => "i8",
403 "uint16" => "i16",
404 "int16" => "i16",
405 "uint32" => "i32",
406 "int32" => "i32",
407 "uint64" => "i64",
408 "int64" => "i64",
409 _ => "i8",
410 }
411 } else {
412 match e.underlying_type.as_str() {
413 "uint8" => "u8",
414 "int8" => "i8",
415 "uint16" => "u16",
416 "int16" => "i16",
417 "uint32" => "u32",
418 "int32" => "i32",
419 "uint64" => "u64",
420 "int64" => "i64",
421 _ => "u8",
422 }
423 })
424 }
425}
426
427pub fn interface_class_name(native: &str) -> &str {
428 native.strip_prefix('I').unwrap_or(native)
429}
430
431fn add_interface_functions(
432 module_classes: &mut BTreeMap<String, Vec<ClassInfo>>,
433 classes_map: &mut HashMap<String, ClassInfo>,
434) {
435 let interfaces: HashMap<String, ClassInfo> = classes_map
436 .values()
437 .filter(|c| c.super_class.as_deref() == Some("Interface"))
438 .map(|c| (c.name.clone(), c.clone()))
439 .collect();
440
441 for classes in module_classes.values_mut() {
442 for class in classes.iter_mut() {
443 if class.super_class.as_deref() == Some("Interface") {
444 continue;
445 }
446
447 let mut added = Vec::new();
448
449 for native in &class.interfaces {
450 let Some(iface) = interfaces.get(interface_class_name(native)) else {
451 continue;
452 };
453
454 for func in &iface.funcs {
455 let callable = func.func_flags & FUNC_NATIVE != 0
456 && func.func_flags & FUNC_BLUEPRINT_EVENT == 0
457 && func.func_flags & FUNC_STATIC == 0
458 && !func.is_static;
459
460 let taken = class
461 .funcs
462 .iter()
463 .chain(&added)
464 .any(|f: &FunctionInfo| f.name == func.name);
465
466 if callable && !taken {
467 let mut func = func.clone();
468 func.interface = Some(native.clone());
469 added.push(func);
470 }
471 }
472 }
473
474 if added.is_empty() {
475 continue;
476 }
477
478 class.funcs.extend(added);
479
480 if let Some(mapped) = classes_map.get_mut(&class.name) {
481 mapped.funcs = class.funcs.clone();
482 }
483 }
484 }
485}