Skip to main content

rusteal_codegen/
type_map.rs

1use crate::schema::ParamInfo;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum ParamDirection {
5    In,
6    Out,
7    InOut,
8    Return,
9}
10
11#[derive(Debug, Clone)]
12pub struct MappedType {
13    pub rust_type: String,
14    pub rust_ffi_type: String,
15    pub cpp_type: String,
16    pub property_getter: String,
17    pub property_setter: String,
18    pub rust_to_ffi: ConversionKind,
19    pub ffi_to_rust: ConversionKind,
20    pub supported: bool,
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum ConversionKind {
25    Identity,
26    IntCast,
27    ObjectRef,
28    StringUtf8,
29    EnumCast,
30    StructOpaque,
31    FName,
32    ContainerArray,
33    ContainerMap,
34    ContainerSet,
35    Delegate,
36    MulticastDelegate,
37}
38
39const SUPPORTED_TYPES: &[&str] = &[
40    "BoolProperty",
41    "Int8Property",
42    "ByteProperty",
43    "Int16Property",
44    "UInt16Property",
45    "IntProperty",
46    "UInt32Property",
47    "Int64Property",
48    "UInt64Property",
49    "FloatProperty",
50    "DoubleProperty",
51    "StrProperty",
52    "NameProperty",
53    "TextProperty",
54    "EnumProperty",
55    "ObjectProperty",
56    "ClassProperty",
57    "StructProperty",
58    "ArrayProperty",
59    "MapProperty",
60    "SetProperty",
61    "SoftObjectProperty",
62    "WeakObjectProperty",
63    "InterfaceProperty",
64    "DelegateProperty",
65    "MulticastInlineDelegateProperty",
66    "MulticastSparseDelegateProperty",
67];
68
69pub fn is_supported_type(prop_type: &str) -> bool {
70    SUPPORTED_TYPES.contains(&prop_type)
71}
72
73pub fn map_property_type(
74    prop_type: &str,
75    class_name: Option<&str>,
76    struct_name: Option<&str>,
77    enum_name: Option<&str>,
78    enum_underlying_type: Option<&str>,
79    meta_class_name: Option<&str>,
80    interface_name: Option<&str>,
81) -> MappedType {
82    match prop_type {
83        "BoolProperty" => MappedType {
84            rust_type: "bool".into(),
85            rust_ffi_type: "bool".into(),
86            cpp_type: "bool".into(),
87            property_getter: "get_bool".into(),
88            property_setter: "set_bool".into(),
89            rust_to_ffi: ConversionKind::Identity,
90            ffi_to_rust: ConversionKind::Identity,
91            supported: true,
92        },
93        "Int8Property" => int_type("i8", "int8"),
94        "ByteProperty" => {
95            if let Some(en) = enum_name {
96                enum_type(en, enum_underlying_type.unwrap_or("uint8"))
97            } else {
98                int_type("u8", "uint8")
99            }
100        }
101        "Int16Property" => int_type("i16", "int16"),
102        "UInt16Property" => int_type("u16", "uint16"),
103        "IntProperty" => int_type("i32", "int32"),
104        "UInt32Property" => int_type("u32", "uint32"),
105        "Int64Property" => int_type("i64", "int64"),
106        "UInt64Property" => int_type("u64", "uint64"),
107        "FloatProperty" => MappedType {
108            rust_type: "f32".into(),
109            rust_ffi_type: "f32".into(),
110            cpp_type: "float".into(),
111            property_getter: "get_f32".into(),
112            property_setter: "set_f32".into(),
113            rust_to_ffi: ConversionKind::Identity,
114            ffi_to_rust: ConversionKind::Identity,
115            supported: true,
116        },
117        "DoubleProperty" => MappedType {
118            rust_type: "f64".into(),
119            rust_ffi_type: "f64".into(),
120            cpp_type: "double".into(),
121            property_getter: "get_f64".into(),
122            property_setter: "set_f64".into(),
123            rust_to_ffi: ConversionKind::Identity,
124            ffi_to_rust: ConversionKind::Identity,
125            supported: true,
126        },
127        "StrProperty" => MappedType {
128            rust_type: "String".into(),
129            rust_ffi_type: "*const u8".into(),
130            cpp_type: "FString".into(),
131            property_getter: "get_string".into(),
132            property_setter: "set_string".into(),
133            rust_to_ffi: ConversionKind::StringUtf8,
134            ffi_to_rust: ConversionKind::StringUtf8,
135            supported: true,
136        },
137        "TextProperty" => MappedType {
138            rust_type: "String".into(),
139            rust_ffi_type: "*const u8".into(),
140            cpp_type: "FText".into(),
141            property_getter: "get_string".into(),
142            property_setter: "set_string".into(),
143            rust_to_ffi: ConversionKind::StringUtf8,
144            ffi_to_rust: ConversionKind::StringUtf8,
145            supported: true,
146        },
147        "NameProperty" => MappedType {
148            rust_type: "rusteal_core::FNameHandle".into(),
149            rust_ffi_type: "rusteal_core::FNameHandle".into(),
150            cpp_type: "FName".into(),
151            property_getter: "get_fname".into(),
152            property_setter: "set_fname".into(),
153            rust_to_ffi: ConversionKind::FName,
154            ffi_to_rust: ConversionKind::FName,
155            supported: true,
156        },
157        "EnumProperty" => {
158            if let Some(en) = enum_name {
159                enum_type(en, enum_underlying_type.unwrap_or("uint8"))
160            } else {
161                unsupported("EnumProperty without enum_name")
162            }
163        }
164        "ObjectProperty" => {
165            if let Some(cls) = class_name {
166                MappedType {
167                    rust_type: format!("rusteal_core::UObjectRef<{cls}>"),
168                    rust_ffi_type: "rusteal_core::UObjectHandle".into(),
169                    cpp_type: format!("{cls}*"),
170                    property_getter: "get_object".into(),
171                    property_setter: "set_object".into(),
172                    rust_to_ffi: ConversionKind::ObjectRef,
173                    ffi_to_rust: ConversionKind::ObjectRef,
174                    supported: true,
175                }
176            } else {
177                MappedType {
178                    rust_type: "rusteal_core::UObjectHandle".into(),
179                    rust_ffi_type: "rusteal_core::UObjectHandle".into(),
180                    cpp_type: "UObject*".into(),
181                    property_getter: "get_object".into(),
182                    property_setter: "set_object".into(),
183                    rust_to_ffi: ConversionKind::Identity,
184                    ffi_to_rust: ConversionKind::Identity,
185                    supported: true,
186                }
187            }
188        }
189        "SoftObjectProperty" | "WeakObjectProperty" => {
190            if let Some(cls) = class_name {
191                MappedType {
192                    rust_type: format!("rusteal_core::UObjectRef<{cls}>"),
193                    rust_ffi_type: "rusteal_core::UObjectHandle".into(),
194                    cpp_type: format!("{cls}*"),
195                    property_getter: "get_object".into(),
196                    property_setter: "set_object".into(),
197                    rust_to_ffi: ConversionKind::ObjectRef,
198                    ffi_to_rust: ConversionKind::ObjectRef,
199                    supported: true,
200                }
201            } else {
202                MappedType {
203                    rust_type: "rusteal_core::UObjectHandle".into(),
204                    rust_ffi_type: "rusteal_core::UObjectHandle".into(),
205                    cpp_type: "UObject*".into(),
206                    property_getter: "get_object".into(),
207                    property_setter: "set_object".into(),
208                    rust_to_ffi: ConversionKind::Identity,
209                    ffi_to_rust: ConversionKind::Identity,
210                    supported: true,
211                }
212            }
213        }
214        "ClassProperty" => {
215            let effective_class = meta_class_name.or(class_name);
216
217            if let Some(cls) = effective_class {
218                MappedType {
219                    rust_type: format!("rusteal_core::SubclassOf<{cls}>"),
220                    rust_ffi_type: "rusteal_core::UObjectHandle".into(),
221                    cpp_type: format!("{cls}*"),
222                    property_getter: "get_object".into(),
223                    property_setter: "set_object".into(),
224                    rust_to_ffi: ConversionKind::ObjectRef,
225                    ffi_to_rust: ConversionKind::ObjectRef,
226                    supported: true,
227                }
228            } else {
229                MappedType {
230                    rust_type: "rusteal_core::UObjectHandle".into(),
231                    rust_ffi_type: "rusteal_core::UObjectHandle".into(),
232                    cpp_type: "UObject*".into(),
233                    property_getter: "get_object".into(),
234                    property_setter: "set_object".into(),
235                    rust_to_ffi: ConversionKind::Identity,
236                    ffi_to_rust: ConversionKind::Identity,
237                    supported: true,
238                }
239            }
240        }
241        "InterfaceProperty" => {
242            if let Some(iface) = interface_name {
243                MappedType {
244                    rust_type: format!("rusteal_core::UObjectRef<{iface}>"),
245                    rust_ffi_type: "rusteal_core::UObjectHandle".into(),
246                    cpp_type: format!("{iface}*"),
247                    property_getter: "get_object".into(),
248                    property_setter: "set_object".into(),
249                    rust_to_ffi: ConversionKind::ObjectRef,
250                    ffi_to_rust: ConversionKind::ObjectRef,
251                    supported: true,
252                }
253            } else {
254                unsupported("InterfaceProperty without interface_name")
255            }
256        }
257        "StructProperty" => {
258            if let Some(sn) = struct_name {
259                MappedType {
260                    rust_type: format!("*const u8 /* {sn} */"),
261                    rust_ffi_type: "*const u8".into(),
262                    cpp_type: format!("F{sn}"),
263                    property_getter: "get_struct".into(),
264                    property_setter: "set_struct".into(),
265                    rust_to_ffi: ConversionKind::StructOpaque,
266                    ffi_to_rust: ConversionKind::StructOpaque,
267                    supported: true,
268                }
269            } else {
270                unsupported("StructProperty without struct_name")
271            }
272        }
273        "ArrayProperty" => MappedType {
274            rust_type: "rusteal_core::UeArray<_>".into(),
275            rust_ffi_type: String::new(),
276            cpp_type: String::new(),
277            property_getter: String::new(),
278            property_setter: String::new(),
279            rust_to_ffi: ConversionKind::ContainerArray,
280            ffi_to_rust: ConversionKind::ContainerArray,
281            supported: true,
282        },
283        "MapProperty" => MappedType {
284            rust_type: "rusteal_core::UeMap<_, _>".into(),
285            rust_ffi_type: String::new(),
286            cpp_type: String::new(),
287            property_getter: String::new(),
288            property_setter: String::new(),
289            rust_to_ffi: ConversionKind::ContainerMap,
290            ffi_to_rust: ConversionKind::ContainerMap,
291            supported: true,
292        },
293        "SetProperty" => MappedType {
294            rust_type: "rusteal_core::UeSet<_>".into(),
295            rust_ffi_type: String::new(),
296            cpp_type: String::new(),
297            property_getter: String::new(),
298            property_setter: String::new(),
299            rust_to_ffi: ConversionKind::ContainerSet,
300            ffi_to_rust: ConversionKind::ContainerSet,
301            supported: true,
302        },
303        "DelegateProperty" => MappedType {
304            rust_type: "/* delegate */".into(),
305            rust_ffi_type: String::new(),
306            cpp_type: String::new(),
307            property_getter: String::new(),
308            property_setter: String::new(),
309            rust_to_ffi: ConversionKind::Delegate,
310            ffi_to_rust: ConversionKind::Delegate,
311            supported: true,
312        },
313        "MulticastInlineDelegateProperty" | "MulticastSparseDelegateProperty" => MappedType {
314            rust_type: "/* multicast delegate */".into(),
315            rust_ffi_type: String::new(),
316            cpp_type: String::new(),
317            property_getter: String::new(),
318            property_setter: String::new(),
319            rust_to_ffi: ConversionKind::MulticastDelegate,
320            ffi_to_rust: ConversionKind::MulticastDelegate,
321            supported: true,
322        },
323        _ => unsupported(prop_type),
324    }
325}
326
327pub fn param_direction(param: &ParamInfo) -> ParamDirection {
328    use crate::schema::*;
329
330    if param.prop_flags & CPF_RETURN_PARM != 0 {
331        return ParamDirection::Return;
332    }
333
334    let is_out = param.prop_flags & CPF_OUT_PARM != 0;
335    let is_const = param.prop_flags & CPF_CONST_PARM != 0;
336    let is_ref = param.prop_flags & CPF_REFERENCE_PARM != 0;
337
338    if is_out && is_const {
339        ParamDirection::In
340    } else if is_out && is_ref {
341        ParamDirection::InOut
342    } else if is_out {
343        ParamDirection::Out
344    } else {
345        ParamDirection::In
346    }
347}
348
349fn int_type(rust: &str, _cpp: &str) -> MappedType {
350    let (getter, setter, ffi_type) = match rust {
351        "i8" => ("get_u8", "set_u8", "u8"),
352        "u8" => ("get_u8", "set_u8", "u8"),
353        "i16" => ("get_i32", "set_i32", "i32"),
354        "u16" => ("get_i32", "set_i32", "i32"),
355        "i32" => ("get_i32", "set_i32", "i32"),
356        "u32" => ("get_i32", "set_i32", "i32"),
357        "i64" => ("get_i64", "set_i64", "i64"),
358        "u64" => ("get_i64", "set_i64", "i64"),
359        _ => ("get_i32", "set_i32", "i32"),
360    };
361
362    let cpp = match rust {
363        "i8" => "int8",
364        "u8" => "uint8",
365        "i16" => "int16",
366        "u16" => "uint16",
367        "i32" => "int32",
368        "u32" => "uint32",
369        "i64" => "int64",
370        "u64" => "uint64",
371        _ => "int32",
372    };
373
374    let needs_cast = rust != ffi_type;
375
376    MappedType {
377        rust_type: rust.into(),
378        rust_ffi_type: ffi_type.into(),
379        cpp_type: cpp.into(),
380        property_getter: getter.into(),
381        property_setter: setter.into(),
382        rust_to_ffi: if needs_cast {
383            ConversionKind::IntCast
384        } else {
385            ConversionKind::Identity
386        },
387        ffi_to_rust: if needs_cast {
388            ConversionKind::IntCast
389        } else {
390            ConversionKind::Identity
391        },
392        supported: true,
393    }
394}
395
396fn enum_type(enum_name: &str, underlying: &str) -> MappedType {
397    let repr = match underlying {
398        "uint8" => "u8",
399        "int8" => "i8",
400        "uint16" => "u16",
401        "int16" => "i16",
402        "uint32" => "u32",
403        "int32" => "i32",
404        "uint64" => "u64",
405        "int64" => "i64",
406        _ => "u8",
407    };
408
409    MappedType {
410        rust_type: enum_name.to_string(),
411        rust_ffi_type: repr.into(),
412        cpp_type: enum_name.to_string(),
413        property_getter: "get_enum".into(),
414        property_setter: "set_enum".into(),
415        rust_to_ffi: ConversionKind::EnumCast,
416        ffi_to_rust: ConversionKind::EnumCast,
417        supported: true,
418    }
419}
420
421fn unsupported(reason: &str) -> MappedType {
422    MappedType {
423        rust_type: format!("/* unsupported: {reason} */"),
424        rust_ffi_type: String::new(),
425        cpp_type: String::new(),
426        property_getter: String::new(),
427        property_setter: String::new(),
428        rust_to_ffi: ConversionKind::Identity,
429        ffi_to_rust: ConversionKind::Identity,
430        supported: false,
431    }
432}
433
434use crate::context::CodegenContext;
435use crate::schema::PropertyInfo;
436
437pub fn container_element_rust_type(
438    inner: &PropertyInfo,
439    ctx: Option<&CodegenContext>,
440) -> Option<String> {
441    match inner.prop_type.as_str() {
442        "BoolProperty" => Some("bool".into()),
443        "Int8Property" => Some("i8".into()),
444        "ByteProperty" => {
445            if let Some(en) = &inner.enum_name {
446                if let Some(ctx) = ctx
447                    && !ctx.enums.contains_key(en.as_str())
448                {
449                    return None;
450                }
451
452                Some(en.clone())
453            } else {
454                Some("u8".into())
455            }
456        }
457        "Int16Property" => Some("i16".into()),
458        "UInt16Property" => Some("u16".into()),
459        "IntProperty" => Some("i32".into()),
460        "UInt32Property" => Some("u32".into()),
461        "Int64Property" => Some("i64".into()),
462        "UInt64Property" => Some("u64".into()),
463        "FloatProperty" => Some("f32".into()),
464        "DoubleProperty" => Some("f64".into()),
465        "StrProperty" | "TextProperty" => Some("String".into()),
466        "NameProperty" => Some("rusteal_core::FNameHandle".into()),
467        "ObjectProperty" | "SoftObjectProperty" | "WeakObjectProperty" => {
468            if let Some(cls) = &inner.class_name {
469                if let Some(ctx) = ctx
470                    && !ctx.classes.contains_key(cls.as_str())
471                {
472                    return None;
473                }
474
475                Some(format!("rusteal_core::UObjectRef<{cls}>"))
476            } else {
477                Some("rusteal_core::UObjectHandle".into())
478            }
479        }
480        "ClassProperty" => {
481            let effective_class = inner
482                .meta_class_name
483                .as_deref()
484                .or(inner.class_name.as_deref());
485
486            if let Some(cls) = effective_class {
487                if let Some(ctx) = ctx
488                    && !ctx.classes.contains_key(cls)
489                {
490                    return None;
491                }
492
493                Some(format!("rusteal_core::SubclassOf<{cls}>"))
494            } else {
495                Some("rusteal_core::UObjectHandle".into())
496            }
497        }
498        "InterfaceProperty" => {
499            if let Some(ref iface) = inner.interface_name {
500                if let Some(ctx) = ctx
501                    && !ctx.classes.contains_key(iface.as_str())
502                {
503                    return None;
504                }
505
506                Some(format!("rusteal_core::UObjectRef<{iface}>"))
507            } else {
508                None
509            }
510        }
511        "EnumProperty" => {
512            if let Some(en) = &inner.enum_name {
513                if let Some(ctx) = ctx
514                    && !ctx.enums.contains_key(en.as_str())
515                {
516                    return None;
517                }
518
519                Some(en.clone())
520            } else {
521                None
522            }
523        }
524        "StructProperty" => {
525            if let Some(sn) = &inner.struct_name {
526                if let Some(ctx) = ctx {
527                    if let Some(si) = ctx.structs.get(sn.as_str()) {
528                        if si.has_static_struct {
529                            Some(format!("rusteal_core::OwnedStruct<{}>", si.cpp_name))
530                        } else {
531                            None
532                        }
533                    } else {
534                        None
535                    }
536                } else {
537                    Some(format!("rusteal_core::OwnedStruct<F{sn}>"))
538                }
539            } else {
540                None
541            }
542        }
543        _ => None,
544    }
545}
546
547pub fn resolve_container_rust_type(
548    prop: &PropertyInfo,
549    ctx: Option<&CodegenContext>,
550) -> Option<String> {
551    match prop.prop_type.as_str() {
552        "ArrayProperty" => {
553            let inner = prop.inner_prop.as_ref()?;
554            let elem_type = container_element_rust_type(inner, ctx)?;
555
556            Some(format!("rusteal_core::UeArray<{elem_type}>"))
557        }
558        "MapProperty" => {
559            let key = prop.key_prop.as_ref()?;
560            let val = prop.value_prop.as_ref()?;
561            let key_type = container_element_rust_type(key, ctx)?;
562            let val_type = container_element_rust_type(val, ctx)?;
563
564            Some(format!("rusteal_core::UeMap<{key_type}, {val_type}>"))
565        }
566        "SetProperty" => {
567            let elem = prop.element_prop.as_ref()?;
568            let elem_type = container_element_rust_type(elem, ctx)?;
569
570            Some(format!("rusteal_core::UeSet<{elem_type}>"))
571        }
572        _ => None,
573    }
574}
575
576pub fn is_container_param(param: &ParamInfo) -> bool {
577    matches!(
578        param.prop_type.as_str(),
579        "ArrayProperty" | "MapProperty" | "SetProperty"
580    )
581}
582
583pub fn container_param_input_type(param: &ParamInfo, ctx: &CodegenContext) -> Option<String> {
584    match param.prop_type.as_str() {
585        "ArrayProperty" => {
586            let inner = param.inner_prop.as_ref()?;
587            let elem = container_element_rust_type(inner, Some(ctx))?;
588
589            Some(format!("&[{elem}]"))
590        }
591        "SetProperty" => {
592            let elem = param.element_prop.as_ref()?;
593            let etype = container_element_rust_type(elem, Some(ctx))?;
594
595            Some(format!("&[{etype}]"))
596        }
597        "MapProperty" => {
598            let key = param.key_prop.as_ref()?;
599            let val = param.value_prop.as_ref()?;
600            let kt = container_element_rust_type(key, Some(ctx))?;
601            let vt = container_element_rust_type(val, Some(ctx))?;
602
603            Some(format!("&[({kt}, {vt})]"))
604        }
605        _ => None,
606    }
607}
608
609pub fn container_param_output_type(param: &ParamInfo, ctx: &CodegenContext) -> Option<String> {
610    match param.prop_type.as_str() {
611        "ArrayProperty" => {
612            let inner = param.inner_prop.as_ref()?;
613            let elem = container_element_rust_type(inner, Some(ctx))?;
614
615            Some(format!("Vec<{elem}>"))
616        }
617        "SetProperty" => {
618            let elem = param.element_prop.as_ref()?;
619            let etype = container_element_rust_type(elem, Some(ctx))?;
620
621            Some(format!("Vec<{etype}>"))
622        }
623        "MapProperty" => {
624            let key = param.key_prop.as_ref()?;
625            let val = param.value_prop.as_ref()?;
626            let kt = container_element_rust_type(key, Some(ctx))?;
627            let vt = container_element_rust_type(val, Some(ctx))?;
628
629            Some(format!("Vec<({kt}, {vt})>"))
630        }
631        _ => None,
632    }
633}
634
635pub fn container_elem_type_str(param: &ParamInfo, ctx: &CodegenContext) -> Option<String> {
636    match param.prop_type.as_str() {
637        "ArrayProperty" => {
638            let inner = param.inner_prop.as_ref()?;
639
640            container_element_rust_type(inner, Some(ctx))
641        }
642        "SetProperty" => {
643            let elem = param.element_prop.as_ref()?;
644
645            container_element_rust_type(elem, Some(ctx))
646        }
647        "MapProperty" => {
648            let key = param.key_prop.as_ref()?;
649            let val = param.value_prop.as_ref()?;
650            let kt = container_element_rust_type(key, Some(ctx))?;
651            let vt = container_element_rust_type(val, Some(ctx))?;
652
653            Some(format!("{kt}, {vt}"))
654        }
655        _ => None,
656    }
657}