1use crate::context::CodegenContext;
14use crate::naming::{strip_bool_prefix, to_snake_case};
15use crate::schema::PropertyInfo;
16use crate::type_map::{self, ConversionKind, MappedType};
17
18pub struct PropertyContext {
20 pub find_prop_fn: String,
22 pub handle_expr: String,
25 pub pre_access: String,
28 pub container_expr: String,
30 pub is_class: bool,
33}
34
35pub fn collect_deduped_properties<'a>(
39 props: &'a [PropertyInfo],
40 ctx: Option<&CodegenContext>,
41) -> (std::collections::HashSet<String>, Vec<&'a PropertyInfo>) {
42 let mut prop_names = std::collections::HashSet::new();
43 let mut deduped = Vec::new();
44
45 for prop in props {
46 if prop.getter.is_some() || prop.setter.is_some() {
48 continue;
49 }
50
51 let mapped = type_map::map_property_type(
52 &prop.prop_type,
53 prop.class_name.as_deref(),
54 prop.struct_name.as_deref(),
55 prop.enum_name.as_deref(),
56 prop.enum_underlying_type.as_deref(),
57 prop.meta_class_name.as_deref(),
58 prop.interface_name.as_deref(),
59 );
60 if !mapped.supported {
61 continue;
62 }
63
64 if matches!(mapped.rust_to_ffi, ConversionKind::ObjectRef) {
66 if let Some(ctx) = ctx {
67 let type_available = match prop.prop_type.as_str() {
68 "ClassProperty" => {
69 let effective = prop.meta_class_name.as_deref().or(prop.class_name.as_deref());
70 effective.map_or(true, |c| ctx.classes.contains_key(c))
71 }
72 "InterfaceProperty" => {
73 prop.interface_name.as_deref().map_or(false, |i| ctx.classes.contains_key(i))
74 }
75 _ => prop.class_name.as_deref().map_or(true, |c| ctx.classes.contains_key(c)),
76 };
77 if !type_available {
78 continue;
79 }
80 }
81 }
82
83 if matches!(mapped.rust_to_ffi, ConversionKind::StructOpaque) {
85 let valid = if let Some(ctx) = ctx {
86 prop.struct_name.as_deref().map_or(false, |sn| {
87 ctx.structs.get(sn).map_or(false, |si| si.has_static_struct)
88 })
89 } else {
90 prop.struct_name.is_some()
91 };
92 if !valid {
93 continue;
94 }
95 }
96
97 if matches!(
99 mapped.rust_to_ffi,
100 ConversionKind::ContainerArray | ConversionKind::ContainerMap | ConversionKind::ContainerSet
101 ) {
102 if type_map::resolve_container_rust_type(prop, ctx).is_none() {
103 continue;
104 }
105 }
109
110 if let Some(ctx) = ctx {
112 match mapped.rust_to_ffi {
113 ConversionKind::EnumCast => {
114 if let Some(en) = &prop.enum_name {
115 if !ctx.enums.contains_key(en.as_str()) {
116 continue;
117 }
118 }
119 }
120 ConversionKind::ObjectRef => {
121 if let Some(cn) = &prop.class_name {
122 if !ctx.classes.contains_key(cn.as_str()) {
123 continue;
124 }
125 }
126 }
127 _ => {}
128 }
129 }
130
131 let rust_name = if prop.prop_type == "BoolProperty" {
132 strip_bool_prefix(&prop.name)
133 } else {
134 to_snake_case(&prop.name)
135 };
136
137 let is_container = matches!(
139 mapped.rust_to_ffi,
140 ConversionKind::ContainerArray | ConversionKind::ContainerMap | ConversionKind::ContainerSet
141 );
142 let is_delegate = matches!(
143 mapped.rust_to_ffi,
144 ConversionKind::Delegate | ConversionKind::MulticastDelegate
145 );
146 let getter_name = if is_container || is_delegate {
147 rust_name.clone()
148 } else {
149 format!("get_{rust_name}")
150 };
151 if prop_names.contains(&getter_name) {
152 continue;
153 }
154 prop_names.insert(getter_name);
155 if !is_container && !is_delegate {
156 prop_names.insert(format!("set_{rust_name}"));
157 }
158 deduped.push(prop);
159 }
160
161 (prop_names, deduped)
162}
163
164pub fn generate_property(
168 out: &mut String,
169 prop: &PropertyInfo,
170 pctx: &PropertyContext,
171 ctx: &CodegenContext,
172 suppress_setters: &std::collections::HashSet<String>,
173) {
174 let mapped = type_map::map_property_type(
175 &prop.prop_type,
176 prop.class_name.as_deref(),
177 prop.struct_name.as_deref(),
178 prop.enum_name.as_deref(),
179 prop.enum_underlying_type.as_deref(),
180 prop.meta_class_name.as_deref(),
181 prop.interface_name.as_deref(),
182 );
183 if !mapped.supported {
184 return;
185 }
186
187 if matches!(
189 mapped.rust_to_ffi,
190 ConversionKind::Delegate | ConversionKind::MulticastDelegate
191 ) {
192 return;
193 }
194
195 let prop_name = &prop.name;
196 let rust_name = if prop.prop_type == "BoolProperty" {
197 strip_bool_prefix(prop_name)
198 } else {
199 to_snake_case(prop_name)
200 };
201 let prop_name_len = prop_name.len();
202 let byte_lit = format!("b\"{}\\0\"", prop_name);
203
204 if prop.array_dim > 1 {
206 generate_fixed_array_property(out, prop, &rust_name, &byte_lit, prop_name_len, pctx, ctx, &mapped);
207 return;
208 }
209
210 if matches!(
213 mapped.rust_to_ffi,
214 ConversionKind::ContainerArray | ConversionKind::ContainerMap | ConversionKind::ContainerSet
215 ) {
216 if pctx.is_class {
217 if let Some(container_type) = type_map::resolve_container_rust_type(prop, Some(ctx)) {
218 generate_container_getter(out, &rust_name, &byte_lit, prop_name_len, pctx, &container_type);
219 }
220 }
221 return;
222 }
223
224 match mapped.rust_to_ffi {
226 ConversionKind::StringUtf8 => {
227 generate_string_getter(out, &rust_name, &byte_lit, prop_name_len, pctx);
228 }
229 ConversionKind::StructOpaque => {
230 let struct_cpp = prop.struct_name.as_deref()
231 .and_then(|sn| ctx.structs.get(sn))
232 .map(|si| si.cpp_name.clone())
233 .unwrap_or_else(|| format!("F{}", prop.struct_name.as_deref().unwrap_or("Unknown")));
234 generate_struct_getter(out, &rust_name, &byte_lit, prop_name_len, pctx, &struct_cpp);
235 let setter_name = format!("set_{rust_name}");
236 if !suppress_setters.contains(&setter_name) {
237 generate_struct_setter(out, &rust_name, &byte_lit, prop_name_len, pctx, &struct_cpp);
238 }
239 return;
240 }
241 ConversionKind::ObjectRef => {
242 generate_object_getter(out, &rust_name, &byte_lit, prop_name_len, pctx, &mapped);
243 }
244 ConversionKind::EnumCast => {
245 let actual_repr = prop
246 .enum_name
247 .as_deref()
248 .and_then(|en| ctx.enum_actual_repr(en))
249 .unwrap_or(&mapped.rust_ffi_type);
250 generate_enum_getter(out, &rust_name, &byte_lit, prop_name_len, pctx, &mapped, actual_repr);
251 }
252 ConversionKind::FName => {
253 generate_fname_getter(out, &rust_name, &byte_lit, prop_name_len, pctx);
254 }
255 ConversionKind::IntCast => {
256 generate_int_cast_getter(out, &rust_name, &byte_lit, prop_name_len, pctx, &mapped);
257 }
258 _ => {
259 generate_primitive_getter(out, &rust_name, &byte_lit, prop_name_len, pctx, &mapped);
260 }
261 }
262
263 let setter_name = format!("set_{rust_name}");
265 if suppress_setters.contains(&setter_name) {
266 return;
267 }
268 match mapped.rust_to_ffi {
269 ConversionKind::StringUtf8 => {
270 generate_string_setter(out, &rust_name, &byte_lit, prop_name_len, pctx);
271 }
272 ConversionKind::StructOpaque => { }
273 ConversionKind::ObjectRef => {
274 generate_object_setter(out, &rust_name, &byte_lit, prop_name_len, pctx, &mapped);
275 }
276 ConversionKind::EnumCast => {
277 generate_enum_setter(out, &rust_name, &byte_lit, prop_name_len, pctx, &mapped);
278 }
279 ConversionKind::FName => {
280 generate_fname_setter(out, &rust_name, &byte_lit, prop_name_len, pctx);
281 }
282 ConversionKind::IntCast => {
283 generate_int_cast_setter(out, &rust_name, &byte_lit, prop_name_len, pctx, &mapped);
284 }
285 _ => {
286 generate_primitive_setter(out, &rust_name, &byte_lit, prop_name_len, pctx, &mapped);
287 }
288 }
289}
290
291pub fn default_value_for(rust_type: &str) -> &'static str {
293 match rust_type {
294 "bool" => "false",
295 "i8" | "u8" | "i16" | "u16" | "i32" | "u32" | "i64" | "u64" => "0",
296 "f32" => "0.0f32",
297 "f64" => "0.0f64",
298 _ => "Default::default()",
299 }
300}
301
302fn emit_prop_lookup(out: &mut String, byte_lit: &str, prop_name_len: usize, pctx: &PropertyContext) {
308 let find = &pctx.find_prop_fn;
309 let handle = &pctx.handle_expr;
310 out.push_str(&format!(
311 " static PROP: std::sync::OnceLock<uika_runtime::FPropertyHandle> = std::sync::OnceLock::new();\n\
312 \x20 let prop = *PROP.get_or_init(|| unsafe {{\n\
313 \x20 ((*uika_runtime::api().reflection).{find})(\n\
314 \x20 {handle}, {byte_lit}.as_ptr(), {prop_name_len}\n\
315 \x20 )\n\
316 \x20 }});\n"
317 ));
318}
319
320fn emit_pre_access(out: &mut String, pctx: &PropertyContext) {
322 if !pctx.pre_access.is_empty() {
323 out.push_str(&format!(" {}\n", pctx.pre_access));
324 }
325}
326
327fn generate_primitive_getter(
332 out: &mut String,
333 rust_name: &str,
334 byte_lit: &str,
335 prop_name_len: usize,
336 pctx: &PropertyContext,
337 mapped: &MappedType,
338) {
339 let rust_type = &mapped.rust_type;
340 let getter = &mapped.property_getter;
341 let default = default_value_for(rust_type);
342 let c = &pctx.container_expr;
343
344 out.push_str(&format!(
345 " fn get_{rust_name}(&self) -> {rust_type} {{\n"
346 ));
347 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
348 emit_pre_access(out, pctx);
349 out.push_str(&format!(
350 " let mut out = {default};\n\
351 \x20 uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).{getter})({c}, prop, &mut out) }}, \"{rust_name}\");\n\
352 \x20 out\n\
353 \x20 }}\n\n"
354 ));
355}
356
357fn generate_int_cast_getter(
358 out: &mut String,
359 rust_name: &str,
360 byte_lit: &str,
361 prop_name_len: usize,
362 pctx: &PropertyContext,
363 mapped: &MappedType,
364) {
365 let rust_type = &mapped.rust_type;
366 let ffi_type = &mapped.rust_ffi_type;
367 let getter = &mapped.property_getter;
368 let default = default_value_for(ffi_type);
369 let c = &pctx.container_expr;
370
371 out.push_str(&format!(
372 " fn get_{rust_name}(&self) -> {rust_type} {{\n"
373 ));
374 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
375 emit_pre_access(out, pctx);
376 out.push_str(&format!(
377 " let mut out = {default};\n\
378 \x20 uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).{getter})({c}, prop, &mut out) }}, \"{rust_name}\");\n\
379 \x20 out as {rust_type}\n\
380 \x20 }}\n\n"
381 ));
382}
383
384fn generate_string_getter(
385 out: &mut String,
386 rust_name: &str,
387 byte_lit: &str,
388 prop_name_len: usize,
389 pctx: &PropertyContext,
390) {
391 let c = &pctx.container_expr;
392
393 out.push_str(&format!(
394 " fn get_{rust_name}(&self) -> String {{\n"
395 ));
396 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
397 emit_pre_access(out, pctx);
398 out.push_str(&format!(
399 " let mut buf = vec![0u8; 512];\n\
400 \x20 let mut out_len: u32 = 0;\n\
401 \x20 uika_runtime::ffi_infallible_ctx(unsafe {{\n\
402 \x20 ((*uika_runtime::api().property).get_string)({c}, prop, buf.as_mut_ptr(), buf.len() as u32, &mut out_len)\n\
403 \x20 }}, \"{rust_name}\");\n\
404 \x20 buf.truncate(out_len as usize);\n\
405 \x20 String::from_utf8_lossy(&buf).into_owned()\n\
406 \x20 }}\n\n"
407 ));
408}
409
410fn generate_object_getter(
411 out: &mut String,
412 rust_name: &str,
413 byte_lit: &str,
414 prop_name_len: usize,
415 pctx: &PropertyContext,
416 mapped: &MappedType,
417) {
418 let rust_type = &mapped.rust_type;
419 let c = &pctx.container_expr;
420
421 out.push_str(&format!(
422 " fn get_{rust_name}(&self) -> {rust_type} {{\n"
423 ));
424 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
425 emit_pre_access(out, pctx);
426 out.push_str(&format!(
427 " let mut raw = uika_runtime::UObjectHandle(std::ptr::null_mut());\n\
428 \x20 uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).get_object)({c}, prop, &mut raw) }}, \"{rust_name}\");\n\
429 \x20 unsafe {{ uika_runtime::UObjectRef::from_raw(raw) }}\n\
430 \x20 }}\n\n"
431 ));
432}
433
434fn generate_enum_getter(
435 out: &mut String,
436 rust_name: &str,
437 byte_lit: &str,
438 prop_name_len: usize,
439 pctx: &PropertyContext,
440 mapped: &MappedType,
441 actual_repr: &str,
442) {
443 let rust_type = &mapped.rust_type;
444 let c = &pctx.container_expr;
445
446 out.push_str(&format!(
447 " fn get_{rust_name}(&self) -> {rust_type} {{\n"
448 ));
449 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
450 emit_pre_access(out, pctx);
451 out.push_str(&format!(
452 " let mut raw: i64 = 0;\n\
453 \x20 uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).get_enum)({c}, prop, &mut raw) }}, \"{rust_name}\");\n\
454 \x20 {rust_type}::from_value(raw as {actual_repr}).expect(\"unknown enum value\")\n\
455 \x20 }}\n\n"
456 ));
457}
458
459fn generate_fname_getter(
460 out: &mut String,
461 rust_name: &str,
462 byte_lit: &str,
463 prop_name_len: usize,
464 pctx: &PropertyContext,
465) {
466 let c = &pctx.container_expr;
467
468 out.push_str(&format!(
469 " fn get_{rust_name}(&self) -> uika_runtime::FNameHandle {{\n"
470 ));
471 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
472 emit_pre_access(out, pctx);
473 out.push_str(&format!(
474 " let mut out = uika_runtime::FNameHandle(0);\n\
475 \x20 uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).get_fname)({c}, prop, &mut out) }}, \"{rust_name}\");\n\
476 \x20 out\n\
477 \x20 }}\n\n"
478 ));
479}
480
481fn generate_primitive_setter(
486 out: &mut String,
487 rust_name: &str,
488 byte_lit: &str,
489 prop_name_len: usize,
490 pctx: &PropertyContext,
491 mapped: &MappedType,
492) {
493 let rust_type = &mapped.rust_type;
494 let setter = &mapped.property_setter;
495 let c = &pctx.container_expr;
496
497 out.push_str(&format!(
498 " fn set_{rust_name}(&self, val: {rust_type}) {{\n"
499 ));
500 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
501 emit_pre_access(out, pctx);
502 out.push_str(&format!(
503 " uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).{setter})({c}, prop, val) }}, \"{rust_name}\");\n\
504 \x20 }}\n\n"
505 ));
506}
507
508fn generate_int_cast_setter(
509 out: &mut String,
510 rust_name: &str,
511 byte_lit: &str,
512 prop_name_len: usize,
513 pctx: &PropertyContext,
514 mapped: &MappedType,
515) {
516 let rust_type = &mapped.rust_type;
517 let ffi_type = &mapped.rust_ffi_type;
518 let setter = &mapped.property_setter;
519 let c = &pctx.container_expr;
520
521 out.push_str(&format!(
522 " fn set_{rust_name}(&self, val: {rust_type}) {{\n"
523 ));
524 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
525 emit_pre_access(out, pctx);
526 out.push_str(&format!(
527 " uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).{setter})({c}, prop, val as {ffi_type}) }}, \"{rust_name}\");\n\
528 \x20 }}\n\n"
529 ));
530}
531
532fn generate_string_setter(
533 out: &mut String,
534 rust_name: &str,
535 byte_lit: &str,
536 prop_name_len: usize,
537 pctx: &PropertyContext,
538) {
539 let c = &pctx.container_expr;
540
541 out.push_str(&format!(
542 " fn set_{rust_name}(&self, val: &str) {{\n"
543 ));
544 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
545 emit_pre_access(out, pctx);
546 out.push_str(&format!(
547 " uika_runtime::ffi_infallible_ctx(unsafe {{\n\
548 \x20 ((*uika_runtime::api().property).set_string)({c}, prop, val.as_ptr(), val.len() as u32)\n\
549 \x20 }}, \"{rust_name}\");\n\
550 \x20 }}\n\n"
551 ));
552}
553
554fn generate_object_setter(
555 out: &mut String,
556 rust_name: &str,
557 byte_lit: &str,
558 prop_name_len: usize,
559 pctx: &PropertyContext,
560 mapped: &MappedType,
561) {
562 let rust_type = &mapped.rust_type;
563 let c = &pctx.container_expr;
564
565 out.push_str(&format!(
566 " fn set_{rust_name}(&self, val: {rust_type}) {{\n"
567 ));
568 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
569 emit_pre_access(out, pctx);
570 out.push_str(&format!(
571 " uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).set_object)({c}, prop, val.raw()) }}, \"{rust_name}\");\n\
572 \x20 }}\n\n"
573 ));
574}
575
576fn generate_enum_setter(
577 out: &mut String,
578 rust_name: &str,
579 byte_lit: &str,
580 prop_name_len: usize,
581 pctx: &PropertyContext,
582 mapped: &MappedType,
583) {
584 let rust_type = &mapped.rust_type;
585 let c = &pctx.container_expr;
586
587 out.push_str(&format!(
588 " fn set_{rust_name}(&self, val: {rust_type}) {{\n"
589 ));
590 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
591 emit_pre_access(out, pctx);
592 out.push_str(&format!(
593 " uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).set_enum)({c}, prop, val as i64) }}, \"{rust_name}\");\n\
594 \x20 }}\n\n"
595 ));
596}
597
598fn generate_fname_setter(
599 out: &mut String,
600 rust_name: &str,
601 byte_lit: &str,
602 prop_name_len: usize,
603 pctx: &PropertyContext,
604) {
605 let c = &pctx.container_expr;
606
607 out.push_str(&format!(
608 " fn set_{rust_name}(&self, val: uika_runtime::FNameHandle) {{\n"
609 ));
610 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
611 emit_pre_access(out, pctx);
612 out.push_str(&format!(
613 " uika_runtime::ffi_infallible_ctx(unsafe {{ ((*uika_runtime::api().property).set_fname)({c}, prop, val) }}, \"{rust_name}\");\n\
614 \x20 }}\n\n"
615 ));
616}
617
618fn generate_struct_getter(
623 out: &mut String,
624 rust_name: &str,
625 byte_lit: &str,
626 prop_name_len: usize,
627 pctx: &PropertyContext,
628 struct_cpp: &str,
629) {
630 let c = &pctx.container_expr;
631
632 out.push_str(&format!(
633 " fn get_{rust_name}(&self) -> uika_runtime::OwnedStruct<{struct_cpp}> {{\n"
634 ));
635 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
636 emit_pre_access(out, pctx);
637 out.push_str(&format!(
638 " let size = unsafe {{ ((*uika_runtime::api().reflection).get_property_size)(prop) }} as usize;\n\
639 \x20 let mut buf = vec![0u8; size];\n\
640 \x20 uika_runtime::ffi_infallible_ctx(unsafe {{\n\
641 \x20 ((*uika_runtime::api().property).get_struct)({c}, prop, buf.as_mut_ptr(), size as u32)\n\
642 \x20 }}, \"{rust_name}\");\n\
643 \x20 uika_runtime::OwnedStruct::from_bytes(buf)\n\
644 \x20 }}\n\n"
645 ));
646}
647
648fn generate_struct_setter(
649 out: &mut String,
650 rust_name: &str,
651 byte_lit: &str,
652 prop_name_len: usize,
653 pctx: &PropertyContext,
654 struct_cpp: &str,
655) {
656 let c = &pctx.container_expr;
657
658 out.push_str(&format!(
659 " fn set_{rust_name}(&self, val: &uika_runtime::OwnedStruct<{struct_cpp}>) {{\n"
660 ));
661 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
662 emit_pre_access(out, pctx);
663 out.push_str(&format!(
664 " uika_runtime::ffi_infallible_ctx(unsafe {{\n\
665 \x20 ((*uika_runtime::api().property).set_struct)({c}, prop, val.as_bytes().as_ptr(), val.as_bytes().len() as u32)\n\
666 \x20 }}, \"{rust_name}\");\n\
667 \x20 }}\n\n"
668 ));
669}
670
671fn generate_fixed_array_property(
676 out: &mut String,
677 prop: &PropertyInfo,
678 rust_name: &str,
679 byte_lit: &str,
680 prop_name_len: usize,
681 pctx: &PropertyContext,
682 ctx: &CodegenContext,
683 mapped: &MappedType,
684) {
685 let array_dim = prop.array_dim;
686 let c = &pctx.container_expr;
687
688 let (getter_ret_type, getter_conversion, setter_param_type, setter_conversion) = match mapped.rust_to_ffi {
690 ConversionKind::Identity if mapped.rust_type == "bool" => (
691 "bool".to_string(),
692 "Ok(buf[0] != 0)".to_string(),
693 "bool".to_string(),
694 format!(
695 "let mut buf = vec![0u8; elem_size];\n\
696 \x20 if val {{ buf[0] = 1; }}"
697 ),
698 ),
699 ConversionKind::Identity | ConversionKind::IntCast => {
700 let rust_type = &mapped.rust_type;
701 let byte_count = rust_type_byte_size(rust_type);
702 (
703 rust_type.clone(),
704 format!("Ok({rust_type}::from_ne_bytes(buf[..{byte_count}].try_into().unwrap()))"),
705 rust_type.clone(),
706 format!("let buf = val.to_ne_bytes().to_vec();"),
707 )
708 }
709 ConversionKind::ObjectRef => {
710 let rust_type = &mapped.rust_type;
711 (
712 rust_type.clone(),
713 "let handle = uika_runtime::UObjectHandle(usize::from_ne_bytes(buf[..std::mem::size_of::<usize>()].try_into().unwrap()) as *mut std::ffi::c_void);\n\
714 \x20 Ok(unsafe { uika_runtime::UObjectRef::from_raw(handle) })".to_string(),
715 rust_type.clone(),
716 "let buf = (val.raw().0 as usize).to_ne_bytes().to_vec();".to_string(),
717 )
718 }
719 ConversionKind::EnumCast => {
720 let rust_type = &mapped.rust_type;
721 let actual_repr = prop
722 .enum_name
723 .as_deref()
724 .and_then(|en| ctx.enum_actual_repr(en))
725 .unwrap_or(&mapped.rust_ffi_type);
726 let byte_count = rust_type_byte_size(actual_repr);
727 (
728 rust_type.clone(),
729 format!(
730 "let raw = {actual_repr}::from_ne_bytes(buf[..{byte_count}].try_into().unwrap());\n\
731 \x20 {rust_type}::from_value(raw).ok_or(uika_runtime::UikaError::TypeMismatch)"
732 ),
733 rust_type.clone(),
734 format!("let buf = (val as {actual_repr}).to_ne_bytes().to_vec();"),
735 )
736 }
737 ConversionKind::StructOpaque => {
738 let struct_cpp = prop.struct_name.as_deref()
739 .and_then(|sn| ctx.structs.get(sn))
740 .map(|si| si.cpp_name.clone())
741 .unwrap_or_else(|| format!("F{}", prop.struct_name.as_deref().unwrap_or("Unknown")));
742 (
743 format!("uika_runtime::OwnedStruct<{struct_cpp}>"),
744 "Ok(uika_runtime::OwnedStruct::from_bytes(buf))".to_string(),
745 format!("&uika_runtime::OwnedStruct<{struct_cpp}>"),
746 "let buf = val.as_bytes().to_vec();".to_string(),
747 )
748 }
749 _ => return, };
751
752 out.push_str(&format!(
754 " fn get_{rust_name}(&self, index: u32) -> uika_runtime::UikaResult<{getter_ret_type}> {{\n"
755 ));
756 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
757 emit_pre_access(out, pctx);
758 out.push_str(&format!(
759 " if index >= {array_dim} {{ return Err(uika_runtime::UikaError::IndexOutOfRange); }}\n\
760 \x20 let elem_size = unsafe {{ ((*uika_runtime::api().reflection).get_element_size)(prop) }} as usize;\n\
761 \x20 let mut buf = vec![0u8; elem_size];\n\
762 \x20 uika_runtime::check_ffi_ctx(unsafe {{\n\
763 \x20 ((*uika_runtime::api().property).get_property_at)({c}, prop, index, buf.as_mut_ptr(), elem_size as u32)\n\
764 \x20 }}, \"{rust_name}\")?;\n\
765 \x20 {getter_conversion}\n\
766 \x20 }}\n\n"
767 ));
768
769 out.push_str(&format!(
771 " fn set_{rust_name}(&self, index: u32, val: {setter_param_type}) -> uika_runtime::UikaResult<()> {{\n"
772 ));
773 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
774 emit_pre_access(out, pctx);
775 out.push_str(&format!(
776 " if index >= {array_dim} {{ return Err(uika_runtime::UikaError::IndexOutOfRange); }}\n\
777 \x20 let elem_size = unsafe {{ ((*uika_runtime::api().reflection).get_element_size)(prop) }} as usize;\n\
778 \x20 {setter_conversion}\n\
779 \x20 uika_runtime::check_ffi_ctx(unsafe {{\n\
780 \x20 ((*uika_runtime::api().property).set_property_at)({c}, prop, index, buf.as_ptr(), elem_size as u32)\n\
781 \x20 }}, \"{rust_name}\")?;\n\
782 \x20 Ok(())\n\
783 \x20 }}\n\n"
784 ));
785}
786
787fn rust_type_byte_size(rust_type: &str) -> usize {
789 match rust_type {
790 "bool" | "i8" | "u8" => 1,
791 "i16" | "u16" => 2,
792 "i32" | "u32" | "f32" => 4,
793 "i64" | "u64" | "f64" => 8,
794 _ => 8, }
796}
797
798fn generate_container_getter(
803 out: &mut String,
804 rust_name: &str,
805 byte_lit: &str,
806 prop_name_len: usize,
807 pctx: &PropertyContext,
808 container_type: &str,
809) {
810 out.push_str(&format!(
811 " fn {rust_name}(&self) -> {container_type} {{\n"
812 ));
813 emit_prop_lookup(out, byte_lit, prop_name_len, pctx);
814 emit_pre_access(out, pctx);
815 let turbofish_type = container_type.replace('<', "::<");
818 out.push_str(&format!(
819 " {turbofish_type}::new(h, prop)\n\
820 \x20 }}\n\n"
821 ));
822}