1use alef_core::ir::{CoreWrapper, PrimitiveType, TypeDef, TypeRef};
2
3use super::ConversionConfig;
4use super::helpers::{
5 core_prim_str, core_type_path_remapped, is_newtype, is_tuple_type_name, needs_f64_cast, needs_i32_cast,
6 needs_i64_cast,
7};
8
9pub fn gen_from_binding_to_core(typ: &TypeDef, core_import: &str) -> String {
13 gen_from_binding_to_core_cfg(typ, core_import, &ConversionConfig::default())
14}
15
16pub fn gen_from_binding_to_core_cfg(typ: &TypeDef, core_import: &str, config: &ConversionConfig) -> String {
18 let core_path = core_type_path_remapped(typ, core_import, config.source_crate_remaps);
19 let binding_name = format!("{}{}", config.type_name_prefix, typ.name);
20
21 if is_newtype(typ) {
23 let field = &typ.fields[0];
24 let newtype_inner_expr = match &field.ty {
25 TypeRef::Named(_) => "val._0.into()".to_string(),
26 TypeRef::Path => "val._0.into()".to_string(),
27 TypeRef::Duration => "std::time::Duration::from_millis(val._0)".to_string(),
28 _ => "val._0".to_string(),
29 };
30 return crate::template_env::render(
31 "conversions/binding_to_core_impl",
32 minijinja::context! {
33 core_path => core_path,
34 binding_name => binding_name,
35 is_newtype => true,
36 newtype_inner_expr => newtype_inner_expr,
37 builder_mode => false,
38 uses_builder_pattern => false,
39 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
40 statements => vec![] as Vec<String>,
41 fields => vec![] as Vec<String>,
42 },
43 );
44 }
45
46 let uses_builder_pattern = (config.option_duration_on_defaults
48 && typ.has_default
49 && typ
50 .fields
51 .iter()
52 .any(|f| !f.optional && matches!(f.ty, TypeRef::Duration)))
53 || (config.optionalize_defaults && typ.has_default);
54
55 let has_optionalized_duration = config.option_duration_on_defaults
60 && typ.has_default
61 && typ
62 .fields
63 .iter()
64 .any(|f| !f.optional && matches!(f.ty, TypeRef::Duration));
65
66 if has_optionalized_duration {
67 let optionalized = config.optionalize_defaults && typ.has_default;
69 let mut statements = Vec::new();
70 for field in &typ.fields {
71 if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
72 continue;
74 }
75 if !config.exclude_types.is_empty()
77 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types)
78 {
79 continue;
80 }
81 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
83 if !field.optional && matches!(field.ty, TypeRef::Duration) {
84 let cast = if config.cast_large_ints_to_i64 { " as u64" } else { "" };
85 statements.push(format!(
86 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = std::time::Duration::from_millis(__v{cast}); }}",
87 field.name
88 ));
89 continue;
90 }
91 let conversion = if optionalized && !field.optional {
92 gen_optionalized_field_to_core(&field.name, &field.ty, config, false)
95 } else {
96 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
102 };
103 let conversion = if binding_name_field != field.name {
105 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
106 } else {
107 conversion
108 };
109 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
111 statements.push(format!("__result.{} = {};", field.name, expr));
112 }
113 }
114
115 return crate::template_env::render(
116 "conversions/binding_to_core_impl",
117 minijinja::context! {
118 core_path => core_path,
119 binding_name => binding_name,
120 is_newtype => false,
121 newtype_inner_expr => "",
122 builder_mode => true,
123 uses_builder_pattern => uses_builder_pattern,
124 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
125 statements => statements,
126 fields => vec![] as Vec<String>,
127 },
128 );
129 }
130
131 let optionalized = config.optionalize_defaults && typ.has_default;
132
133 let mut fields = Vec::new();
135 let mut statements = Vec::new();
136
137 for field in &typ.fields {
138 let references_excluded = !config.exclude_types.is_empty()
150 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types);
151 if references_excluded && typ.has_stripped_cfg_fields {
152 continue;
153 }
154 if field.cfg.is_some()
160 && !config.never_skip_cfg_field_names.contains(&field.name)
161 && config.strip_cfg_fields_from_binding_struct
162 {
163 continue;
164 }
165 if optionalized && ((field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded) {
166 continue;
167 }
168 let field_was_optionalized = optionalized && !field.optional;
169 let conversion = if (field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded {
170 format!("{}: Default::default()", field.name)
171 } else if field_was_optionalized {
172 field_conversion_to_core_cfg(&field.name, &field.ty, false, config)
175 } else {
176 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
177 };
178 let conversion = if let Some(newtype_path) = &field.newtype_wrapper {
184 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
185 match &field.ty {
188 TypeRef::Optional(_) => format!("{}: ({expr}).map({newtype_path})", field.name),
189 TypeRef::Vec(_) => {
190 let inner_expr = if let Some(prefix) = expr.strip_suffix(".collect()") {
195 format!("{prefix}.collect::<Vec<_>>()")
196 } else {
197 expr.to_string()
198 };
199 format!(
200 "{}: ({inner_expr}).into_iter().map({newtype_path}).collect()",
201 field.name
202 )
203 }
204 _ if field.optional => format!("{}: ({expr}).map({newtype_path})", field.name),
205 _ => format!("{}: {newtype_path}({expr})", field.name),
206 }
207 } else {
208 conversion
209 }
210 } else {
211 conversion
212 };
213 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
215 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
216 if field.optional {
217 format!("{}: {}.map(Box::new)", field.name, expr)
219 } else {
220 format!("{}: Box::new({})", field.name, expr)
221 }
222 } else {
223 conversion
224 }
225 } else {
226 conversion
227 };
228 let is_opaque_arc_field = field.core_wrapper == CoreWrapper::Arc
236 && matches!(&field.ty, TypeRef::Named(n) if config
237 .opaque_types
238 .is_some_and(|opaque| opaque.contains(n.as_str())));
239 let is_opaque_no_wrapper_field = field.core_wrapper == CoreWrapper::None
243 && matches!(&field.ty, TypeRef::Named(n) if config
244 .opaque_types
245 .is_some_and(|opaque| opaque.contains(n.as_str())));
246 let conversion = if is_opaque_arc_field {
247 if field.optional {
248 format!("{}: val.{}.map(|v| v.inner)", field.name, field.name)
249 } else {
250 format!("{}: val.{}.inner", field.name, field.name)
251 }
252 } else if is_opaque_no_wrapper_field {
253 if config.trait_bridge_field_is_arc_wrapper(&field.name) {
257 if field.optional {
258 format!("{}: val.{}.map(|v| (*v.inner).clone())", field.name, field.name)
259 } else {
260 format!("{}: (*val.{}.inner).clone()", field.name, field.name)
261 }
262 } else {
263 format!("{}: Default::default()", field.name)
264 }
265 } else {
266 apply_core_wrapper_to_core(
267 &conversion,
268 &field.name,
269 &field.core_wrapper,
270 &field.vec_inner_core_wrapper,
271 field.optional,
272 )
273 };
274 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
278 let conversion = if binding_name_field != field.name {
279 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
280 } else {
281 conversion
282 };
283 if optionalized {
284 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
285 if field_was_optionalized {
286 statements.push(format!(
287 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = {}; }}",
288 field.name,
289 expr.replace(&format!("val.{binding_name_field}"), "__v")
290 ));
291 } else {
292 statements.push(format!("__result.{} = {};", field.name, expr));
293 }
294 }
295 } else {
296 fields.push(conversion);
297 }
298 }
299
300 crate::template_env::render(
304 "conversions/binding_to_core_impl",
305 minijinja::context! {
306 core_path => core_path,
307 binding_name => binding_name,
308 is_newtype => false,
309 newtype_inner_expr => "",
310 builder_mode => optionalized,
311 uses_builder_pattern => uses_builder_pattern,
312 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
313 statements => statements,
314 fields => fields,
315 },
316 )
317}
318
319pub(super) fn gen_optionalized_field_to_core(
324 name: &str,
325 ty: &TypeRef,
326 config: &ConversionConfig,
327 field_is_ir_optional: bool,
328) -> String {
329 match ty {
330 TypeRef::Json if config.json_as_value => {
331 format!("{name}: val.{name}.unwrap_or_default()")
332 }
333 TypeRef::Json => {
334 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
335 }
336 TypeRef::Named(_) => {
337 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
339 }
340 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
341 format!("{name}: val.{name}.map(|v| v as f32).unwrap_or(0.0)")
342 }
343 TypeRef::Primitive(PrimitiveType::F32 | PrimitiveType::F64) => {
344 format!("{name}: val.{name}.unwrap_or(0.0)")
345 }
346 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
347 let core_ty = core_prim_str(p);
348 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
349 }
350 TypeRef::Optional(inner)
351 if config.cast_large_ints_to_i64
352 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
353 {
354 if let TypeRef::Primitive(p) = inner.as_ref() {
355 let core_ty = core_prim_str(p);
356 format!("{name}: val.{name}.map(|v| v as {core_ty})")
357 } else {
358 field_conversion_to_core(name, ty, false)
359 }
360 }
361 TypeRef::Duration if config.cast_large_ints_to_i64 => {
362 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64)).unwrap_or_default()")
363 }
364 TypeRef::Duration => {
365 format!("{name}: val.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
366 }
367 TypeRef::Path => {
368 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
369 }
370 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
371 format!("{name}: val.{name}.map(|s| std::path::PathBuf::from(s))")
373 }
374 TypeRef::Optional(_) => {
375 format!("{name}: val.{name}.map(Some)")
378 }
379 TypeRef::Char => {
381 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
382 }
383 TypeRef::Vec(inner) => match inner.as_ref() {
384 TypeRef::Json => {
385 format!(
386 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
387 )
388 }
389 TypeRef::Named(_) => {
390 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
391 }
392 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
393 let core_ty = core_prim_str(p);
394 format!(
395 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
396 )
397 }
398 _ => format!("{name}: val.{name}.unwrap_or_default()"),
399 },
400 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
401 let k_is_json = matches!(k.as_ref(), TypeRef::Json);
405 let k_expr = if k_is_json {
406 "serde_json::from_str(&k).unwrap_or_default()"
407 } else {
408 "k.into()"
409 };
410 format!(
411 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or(serde_json::json!(v)))).collect()"
412 )
413 }
414 TypeRef::Map(k, _v) if matches!(k.as_ref(), TypeRef::Json) => {
415 format!(
417 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (serde_json::from_str(&k).unwrap_or_default(), v)).collect()"
418 )
419 }
420 TypeRef::Map(k, v) => {
421 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
423 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
424 let val_is_string_enum = matches!(v.as_ref(), TypeRef::Named(n)
425 if config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)));
426 if field_is_ir_optional {
427 if val_is_string_enum {
429 format!(
430 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect())"
431 )
432 } else if has_named_val {
433 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
434 } else if has_named_key {
435 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
436 } else {
437 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
438 }
439 } else if val_is_string_enum {
440 format!(
441 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect()"
442 )
443 } else if has_named_val {
444 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, v.into())).collect()")
445 } else if has_named_key {
446 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k.into(), v)).collect()")
447 } else {
448 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
449 }
450 }
451 _ => {
452 format!("{name}: val.{name}.unwrap_or_default()")
454 }
455 }
456}
457
458pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
460 match ty {
461 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Unit => {
463 format!("{name}: val.{name}")
464 }
465 TypeRef::Bytes => {
470 if optional {
471 format!("{name}: val.{name}.map(|v| v.to_vec().into())")
472 } else {
473 format!("{name}: val.{name}.to_vec().into()")
474 }
475 }
476 TypeRef::Json => {
478 if optional {
479 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
480 } else {
481 format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
482 }
483 }
484 TypeRef::Char => {
486 if optional {
487 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
488 } else {
489 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
490 }
491 }
492 TypeRef::Duration => {
494 if optional {
495 format!("{name}: val.{name}.map(std::time::Duration::from_millis)")
496 } else {
497 format!("{name}: std::time::Duration::from_millis(val.{name})")
498 }
499 }
500 TypeRef::Path => {
502 if optional {
503 format!("{name}: val.{name}.map(Into::into)")
504 } else {
505 format!("{name}: val.{name}.into()")
506 }
507 }
508 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
511 format!("{name}: val.{name}")
512 }
513 TypeRef::Named(_) => {
514 if optional {
515 format!("{name}: val.{name}.map(Into::into)")
516 } else {
517 format!("{name}: val.{name}.into()")
518 }
519 }
520 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
524 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
525 "serde_json::from_str(&k).unwrap_or_default()"
526 } else {
527 "k.into()"
528 };
529 if optional {
530 format!(
531 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
532 )
533 } else {
534 format!(
535 "{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect()"
536 )
537 }
538 }
539 TypeRef::Map(_k, v) if matches!(v.as_ref(), TypeRef::Bytes) => {
542 if optional {
543 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.to_vec().into())).collect())")
544 } else {
545 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v.to_vec().into())).collect()")
546 }
547 }
548 TypeRef::Optional(inner) => match inner.as_ref() {
550 TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
551 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
552 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
553 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
554 }
555 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
556 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
557 "serde_json::from_str(&k).unwrap_or_default()"
558 } else {
559 "k.into()"
560 };
561 format!(
562 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
563 )
564 }
565 _ => format!("{name}: val.{name}"),
566 },
567 TypeRef::Vec(inner) => match inner.as_ref() {
569 TypeRef::Json => {
570 if optional {
571 format!(
572 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
573 )
574 } else {
575 format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
576 }
577 }
578 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
580 format!("{name}: val.{name}")
581 }
582 TypeRef::Named(_) => {
583 if optional {
584 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
585 } else {
586 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
587 }
588 }
589 _ => format!("{name}: val.{name}"),
590 },
591 TypeRef::Map(k, v) => {
594 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
595 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
596 let has_json_val = matches!(v.as_ref(), TypeRef::Json);
597 let has_json_key = matches!(k.as_ref(), TypeRef::Json);
598 let has_vec_named_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n)));
600 let has_vec_json_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
602 if has_json_val || has_json_key || has_named_key || has_named_val || has_vec_named_val || has_vec_json_val {
603 let k_expr = if has_json_key {
607 "serde_json::from_str(&k).unwrap_or(serde_json::Value::String(k))"
608 } else {
609 "k.into()"
610 };
611 let v_expr = if has_json_val {
612 "serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v))"
613 } else if has_named_val {
614 "v.into()"
615 } else if has_vec_named_val {
616 "v.into_iter().map(Into::into).collect()"
617 } else if has_vec_json_val {
618 "v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()"
619 } else {
620 "v"
621 };
622 if optional {
623 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
624 } else {
625 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
626 }
627 } else {
628 let is_string_map = matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String);
632 if is_string_map {
633 if optional {
634 format!(
635 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
636 )
637 } else {
638 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
639 }
640 } else {
641 if optional {
645 if has_named_val {
646 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
647 } else {
648 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
649 }
650 } else {
651 format!("{name}: val.{name}.into_iter().collect()")
652 }
653 }
654 }
655 }
656 }
657}
658
659pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
661 if optional && matches!(ty, TypeRef::Optional(_)) {
665 let inner_expr = field_conversion_to_core_cfg(name, ty, false, config);
668 if let Some(expr) = inner_expr.strip_prefix(&format!("{name}: ")) {
670 return format!("{name}: ({expr}).map(Some)");
671 }
672 return inner_expr;
673 }
674
675 if config.map_uses_jsvalue {
677 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
678 let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
679 let is_map = matches!(ty, TypeRef::Map(_, _));
680 if is_nested_vec || is_map || is_vec_json {
681 if optional {
682 return format!(
683 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
684 );
685 }
686 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
687 }
688 if let TypeRef::Optional(inner) = ty {
689 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
690 let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
691 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
692 if is_inner_nested || is_inner_map || is_inner_vec_json {
693 return format!(
694 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
695 );
696 }
697 }
698 }
699
700 if config.vec_named_to_string {
704 if let TypeRef::Vec(inner) = ty {
705 if matches!(inner.as_ref(), TypeRef::Named(_)) {
706 if optional {
707 return format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())");
708 }
709 return format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()");
710 }
711 }
712 }
713 if config.map_as_string && matches!(ty, TypeRef::Map(_, _)) {
715 return format!("{name}: Default::default()");
716 }
717 if config.map_as_string {
718 if let TypeRef::Optional(inner) = ty {
719 if matches!(inner.as_ref(), TypeRef::Map(_, _)) {
720 return format!("{name}: Default::default()");
721 }
722 }
723 }
724 if let Some(untagged_names) = config.untagged_data_enum_names {
727 let direct_named = matches!(ty, TypeRef::Named(n) if untagged_names.contains(n));
728 let optional_named = matches!(ty, TypeRef::Optional(inner)
729 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
730 let vec_named = matches!(ty, TypeRef::Vec(inner)
731 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
732 let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
733 if matches!(outer.as_ref(), TypeRef::Vec(inner)
734 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n))));
735 if direct_named {
736 if optional {
737 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
738 }
739 return format!("{name}: serde_json::from_value(val.{name}).unwrap_or_default()");
740 }
741 if optional_named {
742 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
743 }
744 if vec_named {
745 if optional {
746 return format!(
747 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
748 );
749 }
750 return format!("{name}: val.{name}.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect()");
751 }
752 if optional_vec_named {
753 return format!(
754 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
755 );
756 }
757 }
758 if config.json_to_string && matches!(ty, TypeRef::Json) {
760 return format!("{name}: Default::default()");
761 }
762 if config.json_as_value && matches!(ty, TypeRef::Json) {
764 return format!("{name}: val.{name}");
765 }
766 if config.json_as_value {
767 if let TypeRef::Optional(inner) = ty {
768 if matches!(inner.as_ref(), TypeRef::Json) {
769 return format!("{name}: val.{name}");
770 }
771 }
772 if let TypeRef::Vec(inner) = ty {
773 if matches!(inner.as_ref(), TypeRef::Json) {
774 if optional {
775 return format!("{name}: val.{name}.unwrap_or_default()");
776 }
777 return format!("{name}: val.{name}");
778 }
779 }
780 if let TypeRef::Map(_k, v) = ty {
781 if matches!(v.as_ref(), TypeRef::Json) {
782 if optional {
783 return format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())");
784 }
785 return format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v)).collect()");
786 }
787 }
788 }
789 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
791 if optional {
792 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
793 }
794 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
795 }
796 if !config.cast_large_ints_to_i64
797 && !config.cast_large_ints_to_f64
798 && !config.cast_uints_to_i32
799 && !config.cast_f32_to_f64
800 && !config.json_to_string
801 && !config.vec_named_to_string
802 && !config.map_as_string
803 && config.from_binding_skip_types.is_empty()
804 {
805 return field_conversion_to_core(name, ty, optional);
806 }
807 match ty {
809 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
810 let core_ty = core_prim_str(p);
811 if optional {
812 format!("{name}: val.{name}.map(|v| v as {core_ty})")
813 } else {
814 format!("{name}: val.{name} as {core_ty}")
815 }
816 }
817 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
819 if optional {
820 format!("{name}: val.{name}.map(|v| v as f32)")
821 } else {
822 format!("{name}: val.{name} as f32")
823 }
824 }
825 TypeRef::Duration if config.cast_large_ints_to_i64 => {
826 if optional {
827 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
828 } else {
829 format!("{name}: std::time::Duration::from_millis(val.{name} as u64)")
830 }
831 }
832 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
833 if let TypeRef::Primitive(p) = inner.as_ref() {
834 let core_ty = core_prim_str(p);
835 format!("{name}: val.{name}.map(|v| v as {core_ty})")
836 } else {
837 field_conversion_to_core(name, ty, optional)
838 }
839 }
840 TypeRef::Vec(inner)
842 if config.cast_large_ints_to_i64
843 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
844 {
845 if let TypeRef::Primitive(p) = inner.as_ref() {
846 let core_ty = core_prim_str(p);
847 if optional {
848 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
849 } else {
850 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
851 }
852 } else {
853 field_conversion_to_core(name, ty, optional)
854 }
855 }
856 TypeRef::Map(_k, v)
858 if config.cast_large_ints_to_i64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
859 {
860 if let TypeRef::Primitive(p) = v.as_ref() {
861 let core_ty = core_prim_str(p);
862 if optional {
863 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
864 } else {
865 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
866 }
867 } else {
868 field_conversion_to_core(name, ty, optional)
869 }
870 }
871 TypeRef::Vec(inner)
873 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
874 {
875 if optional {
876 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
877 } else {
878 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
879 }
880 }
881 TypeRef::Optional(inner)
883 if config.cast_f32_to_f64
884 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
885 {
886 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
887 }
888 TypeRef::Primitive(p) if config.cast_uints_to_i32 && needs_i32_cast(p) => {
890 let core_ty = core_prim_str(p);
891 if optional {
892 format!("{name}: val.{name}.map(|v| v as {core_ty})")
893 } else {
894 format!("{name}: val.{name} as {core_ty}")
895 }
896 }
897 TypeRef::Optional(inner)
899 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
900 {
901 if let TypeRef::Primitive(p) = inner.as_ref() {
902 let core_ty = core_prim_str(p);
903 format!("{name}: val.{name}.map(|v| v as {core_ty})")
904 } else {
905 field_conversion_to_core(name, ty, optional)
906 }
907 }
908 TypeRef::Vec(inner)
910 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
911 {
912 if let TypeRef::Primitive(p) = inner.as_ref() {
913 let core_ty = core_prim_str(p);
914 if optional {
915 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
916 } else {
917 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
918 }
919 } else {
920 field_conversion_to_core(name, ty, optional)
921 }
922 }
923 TypeRef::Primitive(p) if config.cast_large_ints_to_f64 && needs_f64_cast(p) => {
925 let core_ty = core_prim_str(p);
926 if optional {
927 format!("{name}: val.{name}.map(|v| v as {core_ty})")
928 } else {
929 format!("{name}: val.{name} as {core_ty}")
930 }
931 }
932 TypeRef::Optional(inner)
934 if config.cast_large_ints_to_f64
935 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
936 {
937 if let TypeRef::Primitive(p) = inner.as_ref() {
938 let core_ty = core_prim_str(p);
939 format!("{name}: val.{name}.map(|v| v as {core_ty})")
940 } else {
941 field_conversion_to_core(name, ty, optional)
942 }
943 }
944 TypeRef::Vec(inner)
946 if config.cast_large_ints_to_f64
947 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
948 {
949 if let TypeRef::Primitive(p) = inner.as_ref() {
950 let core_ty = core_prim_str(p);
951 if optional {
952 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
953 } else {
954 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
955 }
956 } else {
957 field_conversion_to_core(name, ty, optional)
958 }
959 }
960 TypeRef::Map(_k, v)
962 if config.cast_large_ints_to_f64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
963 {
964 if let TypeRef::Primitive(p) = v.as_ref() {
965 let core_ty = core_prim_str(p);
966 if optional {
967 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
968 } else {
969 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
970 }
971 } else {
972 field_conversion_to_core(name, ty, optional)
973 }
974 }
975 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
978 format!("{name}: Default::default()")
979 }
980 TypeRef::Optional(inner) => match inner.as_ref() {
981 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
982 format!("{name}: Default::default()")
983 }
984 _ => field_conversion_to_core(name, ty, optional),
985 },
986 _ => field_conversion_to_core(name, ty, optional),
988 }
989}
990
991pub fn apply_core_wrapper_to_core(
994 conversion: &str,
995 name: &str,
996 core_wrapper: &CoreWrapper,
997 vec_inner_core_wrapper: &CoreWrapper,
998 optional: bool,
999) -> String {
1000 if *vec_inner_core_wrapper == CoreWrapper::Arc {
1002 return conversion
1003 .replace(
1004 ".map(Into::into).collect()",
1005 ".map(|v| std::sync::Arc::new(v.into())).collect()",
1006 )
1007 .replace(
1008 "map(|v| v.into_iter().map(Into::into)",
1009 "map(|v| v.into_iter().map(|v| std::sync::Arc::new(v.into()))",
1010 );
1011 }
1012
1013 match core_wrapper {
1014 CoreWrapper::None => conversion.to_string(),
1015 CoreWrapper::Cow => {
1016 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1020 if optional {
1021 format!("{name}: {expr}.map(Into::into)")
1022 } else if expr == format!("val.{name}") {
1023 format!("{name}: val.{name}.into()")
1024 } else if expr == "Default::default()" {
1025 conversion.to_string()
1028 } else {
1029 format!("{name}: ({expr}).into()")
1030 }
1031 } else {
1032 conversion.to_string()
1033 }
1034 }
1035 CoreWrapper::Arc => {
1036 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1038 if expr == "Default::default()" {
1039 conversion.to_string()
1042 } else if optional {
1043 format!("{name}: {expr}.map(|v| std::sync::Arc::new(v))")
1044 } else {
1045 format!("{name}: std::sync::Arc::new({expr})")
1046 }
1047 } else {
1048 conversion.to_string()
1049 }
1050 }
1051 CoreWrapper::Bytes => {
1052 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1058 let already_converted_non_opt =
1059 expr == format!("val.{name}.into()") || expr == format!("val.{name}.to_vec().into()");
1060 let already_converted_opt = expr
1061 .strip_prefix(&format!("val.{name}"))
1062 .map(|s| s == ".map(Into::into)" || s == ".map(|v| v.to_vec().into())")
1063 .unwrap_or(false);
1064 if already_converted_non_opt || already_converted_opt {
1065 conversion.to_string()
1067 } else if optional {
1068 format!("{name}: {expr}.map(Into::into)")
1069 } else if expr == format!("val.{name}") {
1070 format!("{name}: val.{name}.into()")
1071 } else if expr == "Default::default()" {
1072 conversion.to_string()
1075 } else {
1076 format!("{name}: ({expr}).into()")
1077 }
1078 } else {
1079 conversion.to_string()
1080 }
1081 }
1082 CoreWrapper::ArcMutex => {
1083 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1085 if optional {
1086 format!("{name}: {expr}.map(|v| std::sync::Arc::new(std::sync::Mutex::new(v.into())))")
1087 } else if expr == format!("val.{name}") {
1088 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(val.{name}.into()))")
1089 } else {
1090 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(({expr}).into()))")
1091 }
1092 } else {
1093 conversion.to_string()
1094 }
1095 }
1096 }
1097}
1098
1099#[cfg(test)]
1100mod tests {
1101 use super::gen_from_binding_to_core;
1102 use super::gen_from_binding_to_core_cfg;
1103 use crate::conversions::ConversionConfig;
1104 use ahash::AHashSet;
1105 use alef_core::ir::{CoreWrapper, DefaultValue, FieldDef, TypeDef, TypeRef};
1106
1107 fn type_with_field(field: FieldDef) -> TypeDef {
1108 TypeDef {
1109 name: "ProcessConfig".to_string(),
1110 rust_path: "crate::ProcessConfig".to_string(),
1111 original_rust_path: String::new(),
1112 fields: vec![field],
1113 methods: vec![],
1114 is_opaque: false,
1115 is_clone: true,
1116 is_copy: false,
1117 doc: String::new(),
1118 cfg: None,
1119 is_trait: false,
1120 has_default: true,
1121 has_stripped_cfg_fields: false,
1122 is_return_type: false,
1123 serde_rename_all: None,
1124 has_serde: true,
1125 super_traits: vec![],
1126 }
1127 }
1128
1129 #[test]
1130 fn sanitized_cow_string_field_converts_to_core() {
1131 let field = FieldDef {
1132 name: "language".to_string(),
1133 ty: TypeRef::String,
1134 optional: false,
1135 default: None,
1136 doc: String::new(),
1137 sanitized: true,
1138 is_boxed: false,
1139 type_rust_path: None,
1140 cfg: None,
1141 typed_default: Some(DefaultValue::Empty),
1142 core_wrapper: CoreWrapper::Cow,
1143 vec_inner_core_wrapper: CoreWrapper::None,
1144 newtype_wrapper: None,
1145 serde_rename: None,
1146 serde_flatten: false,
1147 };
1148
1149 let out = gen_from_binding_to_core(&type_with_field(field), "crate");
1150
1151 assert!(out.contains("language: val.language.into()"));
1152 assert!(!out.contains("language: Default::default()"));
1153 }
1154
1155 #[test]
1159 fn trait_bridge_arc_wrapper_field_forwards_value_not_default() {
1160 let opaque_type_name = "VisitorHandle".to_string();
1161 let mut opaque_set = AHashSet::new();
1162 opaque_set.insert(opaque_type_name.clone());
1163
1164 let field = FieldDef {
1165 name: "visitor".to_string(),
1166 ty: TypeRef::Named(opaque_type_name.clone()),
1167 optional: true,
1168 default: None,
1169 doc: String::new(),
1170 sanitized: false,
1171 is_boxed: false,
1172 type_rust_path: None,
1173 cfg: Some("feature = \"visitor\"".to_string()),
1174 typed_default: None,
1175 core_wrapper: CoreWrapper::None,
1176 vec_inner_core_wrapper: CoreWrapper::None,
1177 newtype_wrapper: None,
1178 serde_rename: None,
1179 serde_flatten: false,
1180 };
1181
1182 let never_skip = vec!["visitor".to_string()];
1183 let arc_wrapper = vec!["visitor".to_string()];
1184
1185 let config = ConversionConfig {
1186 opaque_types: Some(&opaque_set),
1187 never_skip_cfg_field_names: &never_skip,
1188 trait_bridge_arc_wrapper_field_names: &arc_wrapper,
1189 ..ConversionConfig::default()
1190 };
1191
1192 let out = gen_from_binding_to_core_cfg(&type_with_field(field), "crate", &config);
1193
1194 assert!(
1195 out.contains("val.visitor.map(|v| (*v.inner).clone())"),
1196 "expected arc-wrapper clone forwarding, got:\n{out}"
1197 );
1198 assert!(
1199 !out.contains("visitor: Default::default()"),
1200 "must not emit Default::default() for arc-wrapper trait-bridge field, got:\n{out}"
1201 );
1202 }
1203
1204 #[test]
1207 fn opaque_no_wrapper_field_without_arc_flag_emits_default() {
1208 let opaque_type_name = "OpaqueHandle".to_string();
1209 let mut opaque_set = AHashSet::new();
1210 opaque_set.insert(opaque_type_name.clone());
1211
1212 let field = FieldDef {
1213 name: "handle".to_string(),
1214 ty: TypeRef::Named(opaque_type_name.clone()),
1215 optional: false,
1216 default: None,
1217 doc: String::new(),
1218 sanitized: false,
1219 is_boxed: false,
1220 type_rust_path: None,
1221 cfg: None,
1222 typed_default: None,
1223 core_wrapper: CoreWrapper::None,
1224 vec_inner_core_wrapper: CoreWrapper::None,
1225 newtype_wrapper: None,
1226 serde_rename: None,
1227 serde_flatten: false,
1228 };
1229
1230 let config = ConversionConfig {
1231 opaque_types: Some(&opaque_set),
1232 ..ConversionConfig::default()
1234 };
1235
1236 let out = gen_from_binding_to_core_cfg(&type_with_field(field), "crate", &config);
1237
1238 assert!(
1239 out.contains("handle: Default::default()"),
1240 "expected Default::default() for non-arc-wrapper opaque field, got:\n{out}"
1241 );
1242 assert!(
1243 !out.contains("(*val.handle.inner).clone()"),
1244 "must not emit arc-clone for non-arc-wrapper opaque field, got:\n{out}"
1245 );
1246 }
1247}