Skip to main content

rusteal_codegen/
context.rs

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) = &param.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) = &param.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) = &param.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) = &param.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_) = &param.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) = &param.inner_prop {
320            self.walk_prop_deps(inner, current_module, deps);
321        }
322
323        if let Some(key) = &param.key_prop {
324            self.walk_prop_deps(key, current_module, deps);
325        }
326
327        if let Some(value) = &param.value_prop {
328            self.walk_prop_deps(value, current_module, deps);
329        }
330
331        if let Some(element) = &param.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}