Skip to main content

uika_codegen/
defaults.rs

1// Default parameter value parsing: UHT JSON default strings → Rust literal expressions.
2
3use crate::context::CodegenContext;
4use crate::schema::ParamInfo;
5use crate::type_map::{ConversionKind, MappedType};
6
7/// Try to parse a JSON default value string into a Rust literal expression.
8/// Returns None if the type's default value is not parseable (param stays required).
9pub fn parse_default_literal(
10    param: &ParamInfo,
11    mapped: &MappedType,
12    ctx: &CodegenContext,
13) -> Option<String> {
14    let default_str = param.default.as_deref()?;
15
16    // StructOpaque: Tier 3, not supported
17    if mapped.rust_to_ffi == ConversionKind::StructOpaque {
18        return None;
19    }
20
21    match param.prop_type.as_str() {
22        "BoolProperty" => parse_bool_default(default_str),
23        "FloatProperty" => parse_float_default(default_str, "f32"),
24        "DoubleProperty" => parse_float_default(default_str, "f64"),
25        "IntProperty" => parse_int_default(default_str, "i32"),
26        "Int8Property" => parse_int_default(default_str, "i8"),
27        "Int16Property" => parse_int_default(default_str, "i16"),
28        "Int64Property" => parse_int_default(default_str, "i64"),
29        "ByteProperty" => {
30            if param.enum_name.is_some() {
31                parse_enum_default(default_str, param, ctx)
32            } else {
33                parse_int_default(default_str, "u8")
34            }
35        }
36        "UInt16Property" => parse_int_default(default_str, "u16"),
37        "UInt32Property" => parse_int_default(default_str, "u32"),
38        "UInt64Property" => parse_int_default(default_str, "u64"),
39        "EnumProperty" => parse_enum_default(default_str, param, ctx),
40        "ObjectProperty" | "ClassProperty"
41        | "SoftObjectProperty" | "WeakObjectProperty"
42        | "InterfaceProperty" => {
43            parse_object_default(default_str, mapped)
44        }
45        "StrProperty" | "TextProperty" => parse_string_default(default_str),
46        "NameProperty" => parse_fname_default(default_str),
47        _ => None,
48    }
49}
50
51fn parse_bool_default(s: &str) -> Option<String> {
52    match s {
53        "true" | "True" => Some("true".into()),
54        "false" | "False" => Some("false".into()),
55        _ => None,
56    }
57}
58
59fn parse_float_default(s: &str, suffix: &str) -> Option<String> {
60    let _: f64 = s.parse().ok()?;
61    if s.contains('.') {
62        Some(format!("{s}{suffix}"))
63    } else {
64        Some(format!("{s}.0{suffix}"))
65    }
66}
67
68fn parse_int_default(s: &str, suffix: &str) -> Option<String> {
69    let _: i128 = s.parse().ok()?;
70    Some(format!("{s}{suffix}"))
71}
72
73fn parse_object_default(s: &str, mapped: &MappedType) -> Option<String> {
74    if s == "None" {
75        // Check if this is a typed UObjectRef or an untyped UObjectHandle
76        if mapped.rust_to_ffi == ConversionKind::ObjectRef {
77            Some("unsafe { uika_runtime::UObjectRef::from_raw(uika_runtime::UObjectHandle(std::ptr::null_mut())) }".into())
78        } else {
79            // Untyped UObjectHandle (Identity conversion)
80            Some("uika_runtime::UObjectHandle(std::ptr::null_mut())".into())
81        }
82    } else {
83        None
84    }
85}
86
87fn parse_string_default(s: &str) -> Option<String> {
88    let escaped = s.replace('\\', "\\\\").replace('"', "\\\"");
89    Some(format!("\"{escaped}\""))
90}
91
92fn parse_fname_default(s: &str) -> Option<String> {
93    if s.is_empty() || s == "None" {
94        Some("uika_runtime::FNameHandle(0)".into())
95    } else {
96        None
97    }
98}
99
100fn parse_enum_default(
101    s: &str,
102    param: &ParamInfo,
103    ctx: &CodegenContext,
104) -> Option<String> {
105    let enum_name = param.enum_name.as_deref()?;
106    let enum_info = ctx.enums.get(enum_name)?;
107
108    // Look up the actual repr type used in generated from_value
109    let actual_repr = ctx.enum_actual_repr(enum_name).unwrap_or("u8");
110
111    // Search pairs for a matching variant
112    for (variant_name, value) in &enum_info.pairs {
113        // variant_name may be full "EFoo::Bar" or short "Bar"
114        if variant_name == s || variant_name.ends_with(&format!("::{s}")) {
115            return Some(format!(
116                "{enum_name}::from_value({value} as {actual_repr}).expect(\"invalid enum default for {enum_name}\")"
117            ));
118        }
119    }
120    None
121}