Skip to main content

rusteal_codegen/rust_gen/
structs.rs

1// Rust struct generation: opaque markers, UeStruct trait, and property accessors.
2
3use crate::context::CodegenContext;
4use crate::schema::StructInfo;
5
6use super::properties::{self, PropertyContext};
7
8/// Generate Rust code for a single UE struct.
9pub fn generate_struct(s: &StructInfo, ctx: &CodegenContext) -> String {
10    let mut out = String::with_capacity(2048);
11    let name = &s.cpp_name; // Use cpp_name (e.g., "FVector") as the Rust type name
12    let stripped = &s.name; // Stripped name (e.g., "Vector") for lookup
13
14    // Import traits and types
15    out.push_str("use super::*;\n");
16    out.push_str("use rusteal_core::{UeClass, UeStruct, UeEnum};\n");
17    let current_module = ctx
18        .package_to_module
19        .get(&s.package)
20        .map(|s| s.as_str())
21        .unwrap_or("");
22    // Sorted: the generated crate is versioned with the project, so the output
23    // has to be the same on every run (enabled_modules is a HashSet).
24    let mut modules: Vec<&String> = ctx.enabled_modules.iter().collect();
25    modules.sort();
26    for module in modules {
27        if module != current_module {
28            if let Some(feature) = ctx.feature_for_module(module) {
29                out.push_str(&format!("#[cfg(feature = \"{feature}\")]\n"));
30            }
31            out.push_str(&format!("use crate::{module}::*;\n"));
32        }
33    }
34    out.push('\n');
35
36    out.push_str(&format!(
37        "/// Opaque UE struct `{name}`. Layout managed by C++ side.\n\
38         pub struct {name};\n\n"
39    ));
40
41    if s.has_static_struct {
42        let name_bytes = stripped.as_bytes();
43        let name_len = name_bytes.len();
44        let byte_lit = format!("b\"{}\\0\"", stripped);
45
46        out.push_str(&format!(
47            "impl rusteal_core::UeStruct for {name} {{\n\
48             \x20   fn static_struct() -> rusteal_core::UStructHandle {{\n\
49             \x20       static CACHE: std::sync::OnceLock<rusteal_core::UStructHandle> = std::sync::OnceLock::new();\n\
50             \x20       *CACHE.get_or_init(|| unsafe {{\n\
51             \x20           rusteal_core::ffi_dispatch::reflection_find_struct({byte_lit}.as_ptr(), {name_len})\n\
52             \x20       }})\n\
53             \x20   }}\n\
54             }}\n\n"
55        ));
56
57        // Generate property accessors if the struct has properties and static_struct
58        let (_, deduped_props) = properties::collect_deduped_properties(&s.props, Some(ctx));
59        if !deduped_props.is_empty() {
60            let pctx = PropertyContext {
61                find_prop_fn: "find_struct_property".to_string(),
62                handle_expr: format!("{name}::static_struct()"),
63                pre_access: String::new(), // No validity check for structs
64                container_expr: "self.as_ptr()".to_string(),
65                is_class: false,
66            };
67
68            let trait_name = format!("{name}Ext");
69
70            // Generate full method bodies into a temp buffer
71            let no_suppress = std::collections::HashSet::new();
72            let mut body_buf = String::new();
73            for prop in &deduped_props {
74                properties::generate_property(&mut body_buf, prop, &pctx, ctx, &no_suppress);
75            }
76
77            // Trait declaration: extract signatures only (fn ... { → fn ...;)
78            out.push_str(&format!("pub trait {trait_name} {{\n"));
79            for line in body_buf.lines() {
80                if line.starts_with("    fn ") && line.ends_with(" {") {
81                    out.push_str(&line[..line.len() - 2]);
82                    out.push_str(";\n");
83                }
84            }
85            out.push_str("}\n\n");
86
87            // Concrete impl on UStructRef<T> with full bodies
88            out.push_str(&format!(
89                "impl {trait_name} for rusteal_core::UStructRef<{name}> {{\n"
90            ));
91            out.push_str(&body_buf);
92            out.push_str("}\n");
93        }
94    }
95
96    out
97}