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: "uika_runtime::FNameHandle".into(),
181 rust_ffi_type: "uika_runtime::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!("uika_runtime::UObjectRef<{cls}>"),
200 rust_ffi_type: "uika_runtime::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: "uika_runtime::UObjectHandle".into(),
212 rust_ffi_type: "uika_runtime::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!("uika_runtime::UObjectRef<{cls}>"),
228 rust_ffi_type: "uika_runtime::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: "uika_runtime::UObjectHandle".into(),
239 rust_ffi_type: "uika_runtime::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!("uika_runtime::UObjectRef<{cls}>"),
254 rust_ffi_type: "uika_runtime::UObjectHandle".into(),
255 cpp_type: format!("{cls}*"),
256 property_getter: "get_object".into(),
257 property_setter: "set_object".into(),
258 rust_to_ffi: ConversionKind::ObjectRef,
259 ffi_to_rust: ConversionKind::ObjectRef,
260 supported: true,
261 }
262 } else {
263 MappedType {
264 rust_type: "uika_runtime::UObjectHandle".into(),
265 rust_ffi_type: "uika_runtime::UObjectHandle".into(),
266 cpp_type: "UObject*".into(),
267 property_getter: "get_object".into(),
268 property_setter: "set_object".into(),
269 rust_to_ffi: ConversionKind::Identity,
270 ffi_to_rust: ConversionKind::Identity,
271 supported: true,
272 }
273 }
274 }
275 "InterfaceProperty" => {
276 if let Some(iface) = interface_name {
277 MappedType {
278 rust_type: format!("uika_runtime::UObjectRef<{iface}>"),
279 rust_ffi_type: "uika_runtime::UObjectHandle".into(),
280 cpp_type: format!("{iface}*"),
281 property_getter: "get_object".into(),
282 property_setter: "set_object".into(),
283 rust_to_ffi: ConversionKind::ObjectRef,
284 ffi_to_rust: ConversionKind::ObjectRef,
285 supported: true,
286 }
287 } else {
288 unsupported("InterfaceProperty without interface_name")
289 }
290 }
291 "StructProperty" => {
292 if let Some(sn) = struct_name {
293 MappedType {
294 rust_type: format!("*const u8 /* {sn} */"),
295 rust_ffi_type: "*const u8".into(),
296 cpp_type: format!("F{sn}"),
297 property_getter: "get_struct".into(),
298 property_setter: "set_struct".into(),
299 rust_to_ffi: ConversionKind::StructOpaque,
300 ffi_to_rust: ConversionKind::StructOpaque,
301 supported: true,
302 }
303 } else {
304 unsupported("StructProperty without struct_name")
305 }
306 }
307 "ArrayProperty" => MappedType {
308 rust_type: "uika_runtime::UeArray<_>".into(),
309 rust_ffi_type: String::new(),
310 cpp_type: String::new(),
311 property_getter: String::new(),
312 property_setter: String::new(),
313 rust_to_ffi: ConversionKind::ContainerArray,
314 ffi_to_rust: ConversionKind::ContainerArray,
315 supported: true,
316 },
317 "MapProperty" => MappedType {
318 rust_type: "uika_runtime::UeMap<_, _>".into(),
319 rust_ffi_type: String::new(),
320 cpp_type: String::new(),
321 property_getter: String::new(),
322 property_setter: String::new(),
323 rust_to_ffi: ConversionKind::ContainerMap,
324 ffi_to_rust: ConversionKind::ContainerMap,
325 supported: true,
326 },
327 "SetProperty" => MappedType {
328 rust_type: "uika_runtime::UeSet<_>".into(),
329 rust_ffi_type: String::new(),
330 cpp_type: String::new(),
331 property_getter: String::new(),
332 property_setter: String::new(),
333 rust_to_ffi: ConversionKind::ContainerSet,
334 ffi_to_rust: ConversionKind::ContainerSet,
335 supported: true,
336 },
337 "DelegateProperty" => MappedType {
338 rust_type: "/* delegate */".into(),
339 rust_ffi_type: String::new(),
340 cpp_type: String::new(),
341 property_getter: String::new(),
342 property_setter: String::new(),
343 rust_to_ffi: ConversionKind::Delegate,
344 ffi_to_rust: ConversionKind::Delegate,
345 supported: true,
346 },
347 "MulticastInlineDelegateProperty" | "MulticastSparseDelegateProperty" => MappedType {
348 rust_type: "/* multicast delegate */".into(),
349 rust_ffi_type: String::new(),
350 cpp_type: String::new(),
351 property_getter: String::new(),
352 property_setter: String::new(),
353 rust_to_ffi: ConversionKind::MulticastDelegate,
354 ffi_to_rust: ConversionKind::MulticastDelegate,
355 supported: true,
356 },
357 _ => unsupported(prop_type),
358 }
359}
360
361pub fn param_direction(param: &ParamInfo) -> ParamDirection {
363 use crate::schema::*;
364
365 if param.prop_flags & CPF_RETURN_PARM != 0 {
366 return ParamDirection::Return;
367 }
368 let is_out = param.prop_flags & CPF_OUT_PARM != 0;
369 let is_const = param.prop_flags & CPF_CONST_PARM != 0;
370 let is_ref = param.prop_flags & CPF_REFERENCE_PARM != 0;
371
372 if is_out && is_const {
373 ParamDirection::In
375 } else if is_out && is_ref {
376 ParamDirection::InOut
377 } else if is_out {
378 ParamDirection::Out
379 } else {
380 ParamDirection::In
381 }
382}
383
384fn int_type(rust: &str, _cpp: &str) -> MappedType {
385 let (getter, setter, ffi_type) = match rust {
388 "i8" => ("get_u8", "set_u8", "u8"), "u8" => ("get_u8", "set_u8", "u8"),
390 "i16" => ("get_i32", "set_i32", "i32"), "u16" => ("get_i32", "set_i32", "i32"), "i32" => ("get_i32", "set_i32", "i32"),
393 "u32" => ("get_i32", "set_i32", "i32"), "i64" => ("get_i64", "set_i64", "i64"),
395 "u64" => ("get_i64", "set_i64", "i64"), _ => ("get_i32", "set_i32", "i32"),
397 };
398 let cpp = match rust {
399 "i8" => "int8",
400 "u8" => "uint8",
401 "i16" => "int16",
402 "u16" => "uint16",
403 "i32" => "int32",
404 "u32" => "uint32",
405 "i64" => "int64",
406 "u64" => "uint64",
407 _ => "int32",
408 };
409 let needs_cast = rust != ffi_type;
410 MappedType {
411 rust_type: rust.into(),
412 rust_ffi_type: ffi_type.into(),
413 cpp_type: cpp.into(),
414 property_getter: getter.into(),
415 property_setter: setter.into(),
416 rust_to_ffi: if needs_cast { ConversionKind::IntCast } else { ConversionKind::Identity },
417 ffi_to_rust: if needs_cast { ConversionKind::IntCast } else { ConversionKind::Identity },
418 supported: true,
419 }
420}
421
422fn enum_type(enum_name: &str, underlying: &str) -> MappedType {
423 let repr = match underlying {
424 "uint8" => "u8",
425 "int8" => "i8",
426 "uint16" => "u16",
427 "int16" => "i16",
428 "uint32" => "u32",
429 "int32" => "i32",
430 "uint64" => "u64",
431 "int64" => "i64",
432 _ => "u8",
433 };
434 MappedType {
435 rust_type: enum_name.to_string(),
436 rust_ffi_type: repr.into(),
437 cpp_type: enum_name.to_string(),
438 property_getter: "get_enum".into(),
439 property_setter: "set_enum".into(),
440 rust_to_ffi: ConversionKind::EnumCast,
441 ffi_to_rust: ConversionKind::EnumCast,
442 supported: true,
443 }
444}
445
446fn unsupported(reason: &str) -> MappedType {
447 MappedType {
448 rust_type: format!("/* unsupported: {reason} */"),
449 rust_ffi_type: String::new(),
450 cpp_type: String::new(),
451 property_getter: String::new(),
452 property_setter: String::new(),
453 rust_to_ffi: ConversionKind::Identity,
454 ffi_to_rust: ConversionKind::Identity,
455 supported: false,
456 }
457}
458
459use crate::context::CodegenContext;
464use crate::schema::PropertyInfo;
465
466pub fn container_element_rust_type(
471 inner: &PropertyInfo,
472 ctx: Option<&CodegenContext>,
473) -> Option<String> {
474 match inner.prop_type.as_str() {
475 "BoolProperty" => Some("bool".into()),
476 "Int8Property" => Some("i8".into()),
477 "ByteProperty" => {
478 if let Some(en) = &inner.enum_name {
479 if let Some(ctx) = ctx {
480 if !ctx.enums.contains_key(en.as_str()) {
481 return None;
482 }
483 }
484 Some(en.clone())
485 } else {
486 Some("u8".into())
487 }
488 }
489 "Int16Property" => Some("i16".into()),
490 "UInt16Property" => Some("u16".into()),
491 "IntProperty" => Some("i32".into()),
492 "UInt32Property" => Some("u32".into()),
493 "Int64Property" => Some("i64".into()),
494 "UInt64Property" => Some("u64".into()),
495 "FloatProperty" => Some("f32".into()),
496 "DoubleProperty" => Some("f64".into()),
497 "StrProperty" | "TextProperty" => Some("String".into()),
498 "NameProperty" => Some("uika_runtime::FNameHandle".into()),
499 "ObjectProperty" | "SoftObjectProperty" | "WeakObjectProperty" => {
500 if let Some(cls) = &inner.class_name {
501 if let Some(ctx) = ctx {
502 if !ctx.classes.contains_key(cls.as_str()) {
503 return None;
504 }
505 }
506 Some(format!("uika_runtime::UObjectRef<{cls}>"))
507 } else {
508 Some("uika_runtime::UObjectHandle".into())
509 }
510 }
511 "ClassProperty" => {
512 let effective_class = inner.meta_class_name.as_deref().or(inner.class_name.as_deref());
513 if let Some(cls) = effective_class {
514 if let Some(ctx) = ctx {
515 if !ctx.classes.contains_key(cls) {
516 return None;
517 }
518 }
519 Some(format!("uika_runtime::UObjectRef<{cls}>"))
520 } else {
521 Some("uika_runtime::UObjectHandle".into())
522 }
523 }
524 "InterfaceProperty" => {
525 if let Some(ref iface) = inner.interface_name {
526 if let Some(ctx) = ctx {
527 if !ctx.classes.contains_key(iface.as_str()) {
528 return None;
529 }
530 }
531 Some(format!("uika_runtime::UObjectRef<{iface}>"))
532 } else {
533 None
534 }
535 }
536 "EnumProperty" => {
537 if let Some(en) = &inner.enum_name {
538 if let Some(ctx) = ctx {
539 if !ctx.enums.contains_key(en.as_str()) {
540 return None;
541 }
542 }
543 Some(en.clone())
544 } else {
545 None
546 }
547 }
548 "StructProperty" => {
549 if let Some(sn) = &inner.struct_name {
550 if let Some(ctx) = ctx {
551 if let Some(si) = ctx.structs.get(sn.as_str()) {
552 if si.has_static_struct {
553 Some(format!("uika_runtime::OwnedStruct<{}>", si.cpp_name))
554 } else {
555 None }
557 } else {
558 None }
560 } else {
561 Some(format!("uika_runtime::OwnedStruct<F{sn}>"))
562 }
563 } else {
564 None
565 }
566 }
567 _ => None, }
569}
570
571pub fn resolve_container_rust_type(
574 prop: &PropertyInfo,
575 ctx: Option<&CodegenContext>,
576) -> Option<String> {
577 match prop.prop_type.as_str() {
578 "ArrayProperty" => {
579 let inner = prop.inner_prop.as_ref()?;
580 let elem_type = container_element_rust_type(inner, ctx)?;
581 Some(format!("uika_runtime::UeArray<{elem_type}>"))
582 }
583 "MapProperty" => {
584 let key = prop.key_prop.as_ref()?;
585 let val = prop.value_prop.as_ref()?;
586 let key_type = container_element_rust_type(key, ctx)?;
587 let val_type = container_element_rust_type(val, ctx)?;
588 Some(format!("uika_runtime::UeMap<{key_type}, {val_type}>"))
589 }
590 "SetProperty" => {
591 let elem = prop.element_prop.as_ref()?;
592 let elem_type = container_element_rust_type(elem, ctx)?;
593 Some(format!("uika_runtime::UeSet<{elem_type}>"))
594 }
595 _ => None,
596 }
597}