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