Skip to main content

uika_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 uika_runtime::{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    for module in &ctx.enabled_modules {
23        if module != current_module {
24            if let Some(feature) = ctx.feature_for_module(module) {
25                out.push_str(&format!("#[cfg(feature = \"{feature}\")]\n"));
26            }
27            out.push_str(&format!("use crate::{module}::*;\n"));
28        }
29    }
30    out.push('\n');
31
32    out.push_str(&format!(
33        "/// Opaque UE struct `{name}`. Layout managed by C++ side.\n\
34         pub struct {name};\n\n"
35    ));
36
37    if s.has_static_struct {
38        let name_bytes = stripped.as_bytes();
39        let name_len = name_bytes.len();
40        let byte_lit = format!("b\"{}\\0\"", stripped);
41
42        out.push_str(&format!(
43            "impl uika_runtime::UeStruct for {name} {{\n\
44             \x20   fn static_struct() -> uika_runtime::UStructHandle {{\n\
45             \x20       static CACHE: std::sync::OnceLock<uika_runtime::UStructHandle> = std::sync::OnceLock::new();\n\
46             \x20       *CACHE.get_or_init(|| unsafe {{\n\
47             \x20           ((*uika_runtime::api().reflection).find_struct)({byte_lit}.as_ptr(), {name_len})\n\
48             \x20       }})\n\
49             \x20   }}\n\
50             }}\n\n"
51        ));
52
53        // Generate property accessors if the struct has properties and static_struct
54        let (_, deduped_props) = properties::collect_deduped_properties(&s.props, Some(ctx));
55        if !deduped_props.is_empty() {
56            let pctx = PropertyContext {
57                find_prop_fn: "find_struct_property".to_string(),
58                handle_expr: format!("{name}::static_struct()"),
59                pre_access: String::new(), // No validity check for structs
60                container_expr: "self.as_ptr()".to_string(),
61                is_class: false,
62            };
63
64            let trait_name = format!("{name}Ext");
65
66            // Generate full method bodies into a temp buffer
67            let no_suppress = std::collections::HashSet::new();
68            let mut body_buf = String::new();
69            for prop in &deduped_props {
70                properties::generate_property(&mut body_buf, prop, &pctx, ctx, &no_suppress);
71            }
72
73            // Trait declaration: extract signatures only (fn ... { → fn ...;)
74            out.push_str(&format!("pub trait {trait_name} {{\n"));
75            for line in body_buf.lines() {
76                if line.starts_with("    fn ") && line.ends_with(" {") {
77                    out.push_str(&line[..line.len() - 2]);
78                    out.push_str(";\n");
79                }
80            }
81            out.push_str("}\n\n");
82
83            // Concrete impl on UStructRef<T> with full bodies
84            out.push_str(&format!(
85                "impl {trait_name} for uika_runtime::UStructRef<{name}> {{\n"
86            ));
87            out.push_str(&body_buf);
88            out.push_str("}\n");
89        }
90    }
91
92    out
93}