Skip to main content

rusteal_codegen/rust_gen/
mod.rs

1pub mod cargo_toml;
2pub mod classes;
3pub mod delegates;
4pub mod enums;
5pub mod func_ids;
6pub mod manual;
7pub mod module;
8pub mod param_helpers;
9pub mod prelude;
10pub mod properties;
11pub mod structs;
12
13use std::collections::HashSet;
14use std::path::Path;
15
16use crate::context::CodegenContext;
17use crate::write_if_changed;
18
19pub fn generate(ctx: &CodegenContext, out_dir: &Path) {
20    std::fs::create_dir_all(out_dir).expect("Failed to create Rust output directory");
21    remove_disabled_modules(ctx, out_dir);
22
23    for module_name in ctx.enabled_modules.iter() {
24        let module_dir = out_dir.join(module_name);
25        std::fs::create_dir_all(&module_dir).expect("Failed to create module directory");
26
27        let mut written: HashSet<String> = HashSet::new();
28
29        if let Some(module_enums) = ctx.module_enums.get(module_name) {
30            for e in module_enums {
31                let code = enums::generate_enum(e);
32                let filename = crate::naming::to_snake_case(&e.name) + ".rs";
33
34                write_if_changed(&module_dir.join(&filename), &code)
35                    .unwrap_or_else(|err| panic!("Failed to write {filename}: {err}"));
36
37                written.insert(filename);
38            }
39        }
40
41        if let Some(module_structs) = ctx.module_structs.get(module_name) {
42            for s in module_structs {
43                let code = structs::generate_struct(s, ctx);
44                let filename = crate::naming::to_snake_case(&s.name) + ".rs";
45
46                write_if_changed(&module_dir.join(&filename), &code)
47                    .unwrap_or_else(|err| panic!("Failed to write {filename}: {err}"));
48
49                written.insert(filename);
50            }
51        }
52
53        if let Some(module_classes) = ctx.module_classes.get(module_name) {
54            for c in module_classes {
55                let code = classes::generate_class(c, ctx);
56                let filename = crate::naming::to_snake_case(&c.name) + ".rs";
57
58                write_if_changed(&module_dir.join(&filename), &code)
59                    .unwrap_or_else(|err| panic!("Failed to write {filename}: {err}"));
60
61                written.insert(filename);
62            }
63        }
64
65        let mod_code = module::generate_module_mod(
66            module_name,
67            ctx.module_enums.get(module_name).map(|v| v.as_slice()),
68            ctx.module_structs.get(module_name).map(|v| v.as_slice()),
69            ctx.module_classes.get(module_name).map(|v| v.as_slice()),
70        );
71
72        write_if_changed(&module_dir.join("mod.rs"), &mod_code)
73            .expect("Failed to write module mod.rs");
74
75        written.insert("mod.rs".to_string());
76        remove_unwritten(&module_dir, &written);
77    }
78
79    let func_ids_code = func_ids::generate_rust_func_ids(&ctx.func_table);
80
81    write_if_changed(&out_dir.join("func_ids.rs"), &func_ids_code)
82        .expect("Failed to write func_ids.rs");
83
84    let lib_code = module::generate_lib_rs(ctx);
85    write_if_changed(&out_dir.join("lib.rs"), &lib_code).expect("Failed to write lib.rs");
86}
87
88fn remove_unwritten(module_dir: &Path, written: &HashSet<String>) {
89    let Ok(entries) = std::fs::read_dir(module_dir) else {
90        return;
91    };
92
93    for entry in entries.flatten() {
94        let name = entry.file_name().to_string_lossy().into_owned();
95
96        if written.contains(&name) {
97            continue;
98        }
99
100        let path = entry.path();
101
102        let removed = if path.is_dir() {
103            std::fs::remove_dir_all(&path)
104        } else {
105            std::fs::remove_file(&path)
106        };
107
108        removed.unwrap_or_else(|e| panic!("Failed to remove stale {}: {e}", path.display()));
109    }
110}
111
112fn remove_disabled_modules(ctx: &CodegenContext, out_dir: &Path) {
113    let Ok(entries) = std::fs::read_dir(out_dir) else {
114        return;
115    };
116
117    for entry in entries.flatten() {
118        let path = entry.path();
119        let name = entry.file_name().to_string_lossy().into_owned();
120
121        if !path.is_dir() || name == "manual" || ctx.enabled_modules.contains(&name) {
122            continue;
123        }
124
125        let generated = std::fs::read_to_string(path.join("mod.rs"))
126            .is_ok_and(|m| m.starts_with("// Auto-generated by rusteal-codegen."));
127
128        if generated {
129            std::fs::remove_dir_all(&path)
130                .unwrap_or_else(|e| panic!("Failed to remove {}: {e}", path.display()));
131
132            eprintln!("  removed {name}/ (module no longer enabled)");
133        }
134    }
135}