rusteal_codegen/rust_gen/
cargo_toml.rs1use std::collections::{BTreeMap, BTreeSet};
9use std::path::Path;
10
11use crate::config::CodegenConfig;
12use crate::context::CodegenContext;
13
14const README: &str = "\
15# bindings
16
17Rust bindings for the Unreal Engine modules this project uses, generated by
18`rusteal build` from the engine's reflection data, together with the C++
19wrappers in `Plugins/Rusteal/Source/Rusteal/Generated/`.
20
21Do not edit: every file here is overwritten on the next run. Regenerate after
22changing the engine version, the enabled modules or the blocklist in
23`rusteal.toml`, and commit the result — the game crate depends on it.
24";
25
26pub fn write_crate_files(crate_dir: &Path, ctx: &CodegenContext, config: &CodegenConfig) {
28 let manifest = format!(
29 "# Generated by rusteal-codegen. Do not edit.\n\
30 \n\
31 [package]\n\
32 name = \"bindings\"\n\
33 version = \"0.1.0\"\n\
34 edition = \"2024\"\n\
35 publish = false\n\
36 \n\
37 [dependencies]\n\
38 rusteal-core = {{ workspace = true }}\n\
39 rusteal-ffi = {{ workspace = true }}\n\
40 glam = {{ workspace = true }}\n\
41 \n\
42 {features}",
43 features = render_features_section(ctx, config),
44 );
45
46 let manifest_path = crate_dir.join("Cargo.toml");
47 std::fs::write(&manifest_path, manifest)
48 .unwrap_or_else(|e| panic!("Failed to write {}: {e}", manifest_path.display()));
49
50 let readme_path = crate_dir.join("README.md");
51 std::fs::write(&readme_path, README)
52 .unwrap_or_else(|e| panic!("Failed to write {}: {e}", readme_path.display()));
53
54 eprintln!(" Cargo.toml: {} features", config.features.len());
55}
56
57fn render_features_section(ctx: &CodegenContext, config: &CodegenConfig) -> String {
61 let mut feature_to_modules: BTreeMap<String, Vec<String>> = BTreeMap::new();
64 for mapping in config.modules.values() {
65 feature_to_modules
66 .entry(mapping.feature.clone())
67 .or_default()
68 .push(mapping.module.clone());
69 }
70
71 let generated: BTreeSet<String> = config.features.iter().cloned().collect();
72
73 let mut out = String::from("[features]\n");
74 out.push_str(&format!(
75 "default = {}\n",
76 format_dep_array(&generated.iter().cloned().collect::<Vec<_>>())
77 ));
78
79 for feature in &generated {
80 let mut dep_features: BTreeSet<String> = BTreeSet::new();
81 if let Some(modules) = feature_to_modules.get(feature) {
82 for module in modules {
83 if let Some(module_deps) = ctx.module_deps.get(module) {
84 for dep_module in module_deps {
85 if let Some(dep_feature) = ctx.feature_for_module(dep_module)
86 && dep_feature != feature
87 {
88 dep_features.insert(dep_feature.to_string());
89 }
90 }
91 }
92 }
93 }
94 out.push_str(&format!(
95 "{feature} = {}\n",
96 format_dep_array(&dep_features.iter().cloned().collect::<Vec<_>>())
97 ));
98 }
99
100 out
101}
102
103fn format_dep_array(deps: &[String]) -> String {
104 if deps.is_empty() {
105 "[]".to_string()
106 } else {
107 let items: Vec<String> = deps.iter().map(|d| format!("\"{d}\"")).collect();
108 format!("[{}]", items.join(", "))
109 }
110}