Skip to main content

rusteal_codegen/rust_gen/
mod.rs

1// Rust code generation orchestrator.
2
3pub mod enums;
4pub mod structs;
5pub mod classes;
6pub mod properties;
7pub mod delegates;
8pub mod module;
9pub mod func_ids;
10pub mod param_helpers;
11pub mod cargo_toml;
12pub mod manual;
13pub mod prelude;
14
15use std::path::Path;
16
17use crate::context::CodegenContext;
18
19/// Generate all Rust code into the output directory.
20pub fn generate(ctx: &CodegenContext, out_dir: &Path) {
21    // Ensure output directory exists
22    std::fs::create_dir_all(out_dir).expect("Failed to create Rust output directory");
23    remove_disabled_modules(ctx, out_dir);
24
25    // Generate per-module code
26    for module_name in ctx.enabled_modules.iter() {
27        let module_dir = out_dir.join(module_name);
28        // Start from an empty module directory so types dropped by this run (blocklist,
29        // engine upgrade) do not linger as stale files.
30        if module_dir.exists() {
31            std::fs::remove_dir_all(&module_dir).expect("Failed to clear module directory");
32        }
33        std::fs::create_dir_all(&module_dir).expect("Failed to create module directory");
34
35        // Enums
36        if let Some(module_enums) = ctx.module_enums.get(module_name) {
37            for e in module_enums {
38                let code = enums::generate_enum(e);
39                let filename = crate::naming::to_snake_case(&e.name) + ".rs";
40                std::fs::write(module_dir.join(&filename), code)
41                    .unwrap_or_else(|err| panic!("Failed to write {filename}: {err}"));
42            }
43        }
44
45        // Structs
46        if let Some(module_structs) = ctx.module_structs.get(module_name) {
47            for s in module_structs {
48                let code = structs::generate_struct(s, ctx);
49                let filename = crate::naming::to_snake_case(&s.name) + ".rs";
50                std::fs::write(module_dir.join(&filename), code)
51                    .unwrap_or_else(|err| panic!("Failed to write {filename}: {err}"));
52            }
53        }
54
55        // Classes
56        if let Some(module_classes) = ctx.module_classes.get(module_name) {
57            for c in module_classes {
58                let code = classes::generate_class(c, ctx);
59                let filename = crate::naming::to_snake_case(&c.name) + ".rs";
60                std::fs::write(module_dir.join(&filename), code)
61                    .unwrap_or_else(|err| panic!("Failed to write {filename}: {err}"));
62            }
63        }
64
65        // Module mod.rs
66        let mod_code = module::generate_module_mod(
67            module_name,
68            ctx.module_enums.get(module_name).map(|v| v.as_slice()),
69            ctx.module_structs.get(module_name).map(|v| v.as_slice()),
70            ctx.module_classes.get(module_name).map(|v| v.as_slice()),
71        );
72        std::fs::write(module_dir.join("mod.rs"), mod_code)
73            .expect("Failed to write module mod.rs");
74    }
75
76    // Generate func_ids.rs
77    let func_ids_code = func_ids::generate_rust_func_ids(&ctx.func_table);
78    std::fs::write(out_dir.join("func_ids.rs"), func_ids_code)
79        .expect("Failed to write func_ids.rs");
80
81    // Generate top-level lib.rs
82    let lib_code = module::generate_lib_rs(ctx);
83    std::fs::write(out_dir.join("lib.rs"), lib_code).expect("Failed to write lib.rs");
84}
85
86/// Delete the module directories of modules this run does not generate. `lib.rs`
87/// stops declaring a module when its feature is turned off, but its directory
88/// would stay behind. Only directories whose `mod.rs` carries the codegen header
89/// are removed; `manual/` is rewritten by `manual::write_manual_module`.
90fn remove_disabled_modules(ctx: &CodegenContext, out_dir: &Path) {
91    let Ok(entries) = std::fs::read_dir(out_dir) else {
92        return;
93    };
94    for entry in entries.flatten() {
95        let path = entry.path();
96        let name = entry.file_name().to_string_lossy().into_owned();
97        if !path.is_dir() || name == "manual" || ctx.enabled_modules.contains(&name) {
98            continue;
99        }
100        let generated = std::fs::read_to_string(path.join("mod.rs"))
101            .is_ok_and(|m| m.starts_with("// Auto-generated by rusteal-codegen."));
102        if generated {
103            std::fs::remove_dir_all(&path)
104                .unwrap_or_else(|e| panic!("Failed to remove {}: {e}", path.display()));
105            eprintln!("  removed {name}/ (module no longer enabled)");
106        }
107    }
108}