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 \n\
44 {lints}",
45 features = render_features_section(ctx, config),
46 lints = render_lints_section(),
47 );
48
49 let manifest_path = crate_dir.join("Cargo.toml");
50 std::fs::write(&manifest_path, manifest)
51 .unwrap_or_else(|e| panic!("Failed to write {}: {e}", manifest_path.display()));
52
53 let readme_path = crate_dir.join("README.md");
54 std::fs::write(&readme_path, README)
55 .unwrap_or_else(|e| panic!("Failed to write {}: {e}", readme_path.display()));
56
57 eprintln!(" Cargo.toml: {} features", config.features.len());
58}
59
60fn render_features_section(ctx: &CodegenContext, config: &CodegenConfig) -> String {
64 let mut feature_to_modules: BTreeMap<String, Vec<String>> = BTreeMap::new();
67 for mapping in config.modules.values() {
68 feature_to_modules
69 .entry(mapping.feature.clone())
70 .or_default()
71 .push(mapping.module.clone());
72 }
73
74 let generated: BTreeSet<String> = config.features.iter().cloned().collect();
75
76 let mut out = String::from("[features]\n");
77 out.push_str(&format!(
78 "default = {}\n",
79 format_dep_array(&generated.iter().cloned().collect::<Vec<_>>())
80 ));
81
82 for feature in &generated {
83 let mut dep_features: BTreeSet<String> = BTreeSet::new();
84 if let Some(modules) = feature_to_modules.get(feature) {
85 for module in modules {
86 if let Some(module_deps) = ctx.module_deps.get(module) {
87 for dep_module in module_deps {
88 if let Some(dep_feature) = ctx.feature_for_module(dep_module)
89 && dep_feature != feature
90 {
91 dep_features.insert(dep_feature.to_string());
92 }
93 }
94 }
95 }
96 }
97 out.push_str(&format!(
98 "{feature} = {}\n",
99 format_dep_array(&dep_features.iter().cloned().collect::<Vec<_>>())
100 ));
101 }
102
103 out
104}
105
106fn render_lints_section() -> String {
109 let features: Vec<String> = super::manual::gating_features()
110 .into_iter()
111 .map(|f| format!("\"{f}\""))
112 .collect();
113 format!(
114 "[lints.rust]\n\
115 unexpected_cfgs = {{ level = \"warn\", check-cfg = ['cfg(feature, values({}))'] }}\n",
116 features.join(", ")
117 )
118}
119
120fn format_dep_array(deps: &[String]) -> String {
121 if deps.is_empty() {
122 "[]".to_string()
123 } else {
124 let items: Vec<String> = deps.iter().map(|d| format!("\"{d}\"")).collect();
125 format!("[{}]", items.join(", "))
126 }
127}