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}