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