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