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.binding_excluded {
72 continue;
73 }
74 if field.sanitized && field.core_wrapper != CoreWrapper::Cow {
75 continue;
77 }
78 if !config.exclude_types.is_empty()
80 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types)
81 {
82 continue;
83 }
84 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
86 if !field.optional && matches!(field.ty, TypeRef::Duration) {
87 let cast = if config.cast_large_ints_to_i64 { " as u64" } else { "" };
88 statements.push(format!(
89 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = std::time::Duration::from_millis(__v{cast}); }}",
90 field.name
91 ));
92 continue;
93 }
94 let conversion = if optionalized && !field.optional {
95 gen_optionalized_field_to_core(&field.name, &field.ty, config, false)
98 } else {
99 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
105 };
106 let conversion = if binding_name_field != field.name {
108 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
109 } else {
110 conversion
111 };
112 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
114 statements.push(format!("__result.{} = {};", field.name, expr));
115 }
116 }
117
118 return crate::template_env::render(
119 "conversions/binding_to_core_impl",
120 minijinja::context! {
121 core_path => core_path,
122 binding_name => binding_name,
123 is_newtype => false,
124 newtype_inner_expr => "",
125 builder_mode => true,
126 uses_builder_pattern => uses_builder_pattern,
127 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
128 statements => statements,
129 fields => vec![] as Vec<String>,
130 },
131 );
132 }
133
134 let optionalized = config.optionalize_defaults && typ.has_default;
135
136 let mut fields = Vec::new();
138 let mut statements = Vec::new();
139
140 for field in &typ.fields {
141 if field.binding_excluded {
142 fields.push(format!("{}: Default::default()", field.name));
143 continue;
144 }
145 let references_excluded = !config.exclude_types.is_empty()
157 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types);
158 if references_excluded && typ.has_stripped_cfg_fields {
159 continue;
160 }
161 if field.cfg.is_some()
167 && !config.never_skip_cfg_field_names.contains(&field.name)
168 && config.strip_cfg_fields_from_binding_struct
169 {
170 continue;
171 }
172 if optionalized && ((field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded) {
173 continue;
174 }
175 let field_was_optionalized = optionalized && !field.optional;
176 let conversion = if (field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded {
177 format!("{}: Default::default()", field.name)
178 } else if field_was_optionalized {
179 field_conversion_to_core_cfg(&field.name, &field.ty, false, config)
182 } else {
183 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
184 };
185 let conversion = if let Some(newtype_path) = &field.newtype_wrapper {
191 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
192 match &field.ty {
195 TypeRef::Optional(_) => format!("{}: ({expr}).map({newtype_path})", field.name),
196 TypeRef::Vec(_) => {
197 let inner_expr = if let Some(prefix) = expr.strip_suffix(".collect()") {
202 format!("{prefix}.collect::<Vec<_>>()")
203 } else {
204 expr.to_string()
205 };
206 format!(
207 "{}: ({inner_expr}).into_iter().map({newtype_path}).collect()",
208 field.name
209 )
210 }
211 _ if field.optional => format!("{}: ({expr}).map({newtype_path})", field.name),
212 _ => format!("{}: {newtype_path}({expr})", field.name),
213 }
214 } else {
215 conversion
216 }
217 } else {
218 conversion
219 };
220 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
222 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
223 if field.optional {
224 format!("{}: {}.map(Box::new)", field.name, expr)
226 } else {
227 format!("{}: Box::new({})", field.name, expr)
228 }
229 } else {
230 conversion
231 }
232 } else {
233 conversion
234 };
235 let is_opaque_arc_field = field.core_wrapper == CoreWrapper::Arc
243 && matches!(&field.ty, TypeRef::Named(n) if config
244 .opaque_types
245 .is_some_and(|opaque| opaque.contains(n.as_str())));
246 let is_opaque_no_wrapper_field = field.core_wrapper == CoreWrapper::None
250 && matches!(&field.ty, TypeRef::Named(n) if config
251 .opaque_types
252 .is_some_and(|opaque| opaque.contains(n.as_str())));
253 let conversion = if is_opaque_arc_field {
254 if field.optional {
255 format!("{}: val.{}.map(|v| v.inner)", field.name, field.name)
256 } else {
257 format!("{}: val.{}.inner", field.name, field.name)
258 }
259 } else if is_opaque_no_wrapper_field {
260 if config.trait_bridge_field_is_arc_wrapper(&field.name) {
264 if field.optional {
265 format!("{}: val.{}.map(|v| (*v.inner).clone())", field.name, field.name)
266 } else {
267 format!("{}: (*val.{}.inner).clone()", field.name, field.name)
268 }
269 } else {
270 format!("{}: Default::default()", field.name)
271 }
272 } else {
273 apply_core_wrapper_to_core(
274 &conversion,
275 &field.name,
276 &field.core_wrapper,
277 &field.vec_inner_core_wrapper,
278 field.optional,
279 )
280 };
281 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
285 let conversion = if binding_name_field != field.name {
286 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
287 } else {
288 conversion
289 };
290 if optionalized {
291 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
292 if field_was_optionalized {
293 statements.push(format!(
294 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = {}; }}",
295 field.name,
296 expr.replace(&format!("val.{binding_name_field}"), "__v")
297 ));
298 } else {
299 statements.push(format!("__result.{} = {};", field.name, expr));
300 }
301 }
302 } else {
303 fields.push(conversion);
304 }
305 }
306
307 crate::template_env::render(
311 "conversions/binding_to_core_impl",
312 minijinja::context! {
313 core_path => core_path,
314 binding_name => binding_name,
315 is_newtype => false,
316 newtype_inner_expr => "",
317 builder_mode => optionalized,
318 uses_builder_pattern => uses_builder_pattern,
319 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
320 statements => statements,
321 fields => fields,
322 },
323 )
324}
325
326pub(super) fn gen_optionalized_field_to_core(
331 name: &str,
332 ty: &TypeRef,
333 config: &ConversionConfig,
334 field_is_ir_optional: bool,
335) -> String {
336 match ty {
337 TypeRef::Json if config.json_as_value => {
338 format!("{name}: val.{name}.unwrap_or_default()")
339 }
340 TypeRef::Json => {
341 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
342 }
343 TypeRef::Named(_) => {
344 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
346 }
347 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
348 format!("{name}: val.{name}.map(|v| v as f32).unwrap_or(0.0)")
349 }
350 TypeRef::Primitive(PrimitiveType::F32 | PrimitiveType::F64) => {
351 format!("{name}: val.{name}.unwrap_or(0.0)")
352 }
353 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
354 let core_ty = core_prim_str(p);
355 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
356 }
357 TypeRef::Optional(inner)
358 if config.cast_large_ints_to_i64
359 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
360 {
361 if let TypeRef::Primitive(p) = inner.as_ref() {
362 let core_ty = core_prim_str(p);
363 format!("{name}: val.{name}.map(|v| v as {core_ty})")
364 } else {
365 field_conversion_to_core(name, ty, false)
366 }
367 }
368 TypeRef::Duration if config.cast_large_ints_to_i64 => {
369 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64)).unwrap_or_default()")
370 }
371 TypeRef::Duration => {
372 format!("{name}: val.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
373 }
374 TypeRef::Path => {
375 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
376 }
377 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
378 format!("{name}: val.{name}.map(|s| std::path::PathBuf::from(s))")
380 }
381 TypeRef::Optional(_) => {
382 format!("{name}: val.{name}.map(Some)")
385 }
386 TypeRef::Char => {
388 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
389 }
390 TypeRef::Vec(inner) => match inner.as_ref() {
391 TypeRef::Json => {
392 format!(
393 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
394 )
395 }
396 TypeRef::Named(_) => {
397 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
398 }
399 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
400 let core_ty = core_prim_str(p);
401 format!(
402 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
403 )
404 }
405 _ => format!("{name}: val.{name}.unwrap_or_default()"),
406 },
407 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
408 let k_is_json = matches!(k.as_ref(), TypeRef::Json);
412 let k_expr = if k_is_json {
413 "serde_json::from_str(&k).unwrap_or_default()"
414 } else {
415 "k.into()"
416 };
417 format!(
418 "{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()"
419 )
420 }
421 TypeRef::Map(k, _v) if matches!(k.as_ref(), TypeRef::Json) => {
422 format!(
424 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (serde_json::from_str(&k).unwrap_or_default(), v)).collect()"
425 )
426 }
427 TypeRef::Map(k, v) => {
428 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
430 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
431 let val_is_string_enum = matches!(v.as_ref(), TypeRef::Named(n)
432 if config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)));
433 if field_is_ir_optional {
434 if val_is_string_enum {
436 format!(
437 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect())"
438 )
439 } else if has_named_val {
440 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
441 } else if has_named_key {
442 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
443 } else {
444 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
445 }
446 } else if val_is_string_enum {
447 format!(
448 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect()"
449 )
450 } else if has_named_val {
451 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, v.into())).collect()")
452 } else if has_named_key {
453 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k.into(), v)).collect()")
454 } else {
455 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
456 }
457 }
458 _ => {
459 format!("{name}: val.{name}.unwrap_or_default()")
461 }
462 }
463}
464
465pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
467 match ty {
468 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Unit => {
470 format!("{name}: val.{name}")
471 }
472 TypeRef::Bytes => {
477 if optional {
478 format!("{name}: val.{name}.map(|v| v.to_vec().into())")
479 } else {
480 format!("{name}: val.{name}.to_vec().into()")
481 }
482 }
483 TypeRef::Json => {
485 if optional {
486 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
487 } else {
488 format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
489 }
490 }
491 TypeRef::Char => {
493 if optional {
494 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
495 } else {
496 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
497 }
498 }
499 TypeRef::Duration => {
501 if optional {
502 format!("{name}: val.{name}.map(std::time::Duration::from_millis)")
503 } else {
504 format!("{name}: std::time::Duration::from_millis(val.{name})")
505 }
506 }
507 TypeRef::Path => {
509 if optional {
510 format!("{name}: val.{name}.map(Into::into)")
511 } else {
512 format!("{name}: val.{name}.into()")
513 }
514 }
515 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
518 format!("{name}: val.{name}")
519 }
520 TypeRef::Named(_) => {
521 if optional {
522 format!("{name}: val.{name}.map(Into::into)")
523 } else {
524 format!("{name}: val.{name}.into()")
525 }
526 }
527 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
531 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
532 "serde_json::from_str(&k).unwrap_or_default()"
533 } else {
534 "k.into()"
535 };
536 if optional {
537 format!(
538 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
539 )
540 } else {
541 format!(
542 "{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect()"
543 )
544 }
545 }
546 TypeRef::Map(_k, v) if matches!(v.as_ref(), TypeRef::Bytes) => {
549 if optional {
550 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.to_vec().into())).collect())")
551 } else {
552 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v.to_vec().into())).collect()")
553 }
554 }
555 TypeRef::Optional(inner) => match inner.as_ref() {
557 TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
558 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
559 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
560 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
561 }
562 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
563 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
564 "serde_json::from_str(&k).unwrap_or_default()"
565 } else {
566 "k.into()"
567 };
568 format!(
569 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
570 )
571 }
572 _ => format!("{name}: val.{name}"),
573 },
574 TypeRef::Vec(inner) => match inner.as_ref() {
576 TypeRef::Json => {
577 if optional {
578 format!(
579 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
580 )
581 } else {
582 format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
583 }
584 }
585 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
587 format!("{name}: val.{name}")
588 }
589 TypeRef::Named(_) => {
590 if optional {
591 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
592 } else {
593 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
594 }
595 }
596 _ => format!("{name}: val.{name}"),
597 },
598 TypeRef::Map(k, v) => {
601 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
602 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
603 let has_json_val = matches!(v.as_ref(), TypeRef::Json);
604 let has_json_key = matches!(k.as_ref(), TypeRef::Json);
605 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)));
607 let has_vec_json_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
609 if has_json_val || has_json_key || has_named_key || has_named_val || has_vec_named_val || has_vec_json_val {
610 let k_expr = if has_json_key {
614 "serde_json::from_str(&k).unwrap_or(serde_json::Value::String(k))"
615 } else {
616 "k.into()"
617 };
618 let v_expr = if has_json_val {
619 "serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v))"
620 } else if has_named_val {
621 "v.into()"
622 } else if has_vec_named_val {
623 "v.into_iter().map(Into::into).collect()"
624 } else if has_vec_json_val {
625 "v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()"
626 } else {
627 "v"
628 };
629 if optional {
630 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
631 } else {
632 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
633 }
634 } else {
635 let is_string_map = matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String);
639 if is_string_map {
640 if optional {
641 format!(
642 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
643 )
644 } else {
645 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
646 }
647 } else {
648 if optional {
652 if has_named_val {
653 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
654 } else {
655 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
656 }
657 } else {
658 format!("{name}: val.{name}.into_iter().collect()")
659 }
660 }
661 }
662 }
663 }
664}
665
666pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
668 if optional && matches!(ty, TypeRef::Optional(_)) {
672 let inner_expr = field_conversion_to_core_cfg(name, ty, false, config);
675 if let Some(expr) = inner_expr.strip_prefix(&format!("{name}: ")) {
677 return format!("{name}: ({expr}).map(Some)");
678 }
679 return inner_expr;
680 }
681
682 if config.map_uses_jsvalue {
684 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
685 let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
686 let is_map = matches!(ty, TypeRef::Map(_, _));
687 if is_nested_vec || is_map || is_vec_json {
688 if optional {
689 return format!(
690 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
691 );
692 }
693 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
694 }
695 if let TypeRef::Optional(inner) = ty {
696 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
697 let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
698 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
699 if is_inner_nested || is_inner_map || is_inner_vec_json {
700 return format!(
701 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
702 );
703 }
704 }
705 }
706
707 if config.vec_named_to_string {
711 if let TypeRef::Vec(inner) = ty {
712 if matches!(inner.as_ref(), TypeRef::Named(_)) {
713 if optional {
714 return format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())");
715 }
716 return format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()");
717 }
718 }
719 }
720 if config.map_as_string && matches!(ty, TypeRef::Map(_, _)) {
722 return format!("{name}: Default::default()");
723 }
724 if config.map_as_string {
725 if let TypeRef::Optional(inner) = ty {
726 if matches!(inner.as_ref(), TypeRef::Map(_, _)) {
727 return format!("{name}: Default::default()");
728 }
729 }
730 }
731 if config.map_uses_jsvalue {
736 if let Some(tagged_names) = config.tagged_data_enum_names {
737 let bare_named = matches!(ty, TypeRef::Named(n) if tagged_names.contains(n));
738 let optional_named = matches!(ty, TypeRef::Optional(inner)
739 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n)));
740 let vec_named = matches!(ty, TypeRef::Vec(inner)
741 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n)));
742 let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
743 if matches!(outer.as_ref(), TypeRef::Vec(inner)
744 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n))));
745 if bare_named {
746 if optional {
747 return format!(
750 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
751 );
752 }
753 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
755 }
756 if optional_named {
757 return format!(
759 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
760 );
761 }
762 if vec_named {
763 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
764 }
765 if optional_vec_named {
766 return format!(
767 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
768 );
769 }
770 }
771 }
772
773 if let Some(untagged_names) = config.untagged_data_enum_names {
776 let direct_named = matches!(ty, TypeRef::Named(n) if untagged_names.contains(n));
777 let optional_named = matches!(ty, TypeRef::Optional(inner)
778 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
779 let vec_named = matches!(ty, TypeRef::Vec(inner)
780 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
781 let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
782 if matches!(outer.as_ref(), TypeRef::Vec(inner)
783 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n))));
784 if direct_named {
785 if optional {
786 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
787 }
788 return format!("{name}: serde_json::from_value(val.{name}).unwrap_or_default()");
789 }
790 if optional_named {
791 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
792 }
793 if vec_named {
794 if optional {
795 return format!(
796 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
797 );
798 }
799 return format!("{name}: val.{name}.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect()");
800 }
801 if optional_vec_named {
802 return format!(
803 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
804 );
805 }
806 }
807 if config.json_to_string && matches!(ty, TypeRef::Json) {
809 return format!("{name}: Default::default()");
810 }
811 if config.json_as_value && matches!(ty, TypeRef::Json) {
813 return format!("{name}: val.{name}");
814 }
815 if config.json_as_value {
816 if let TypeRef::Optional(inner) = ty {
817 if matches!(inner.as_ref(), TypeRef::Json) {
818 return format!("{name}: val.{name}");
819 }
820 }
821 if let TypeRef::Vec(inner) = ty {
822 if matches!(inner.as_ref(), TypeRef::Json) {
823 if optional {
824 return format!("{name}: val.{name}.unwrap_or_default()");
825 }
826 return format!("{name}: val.{name}");
827 }
828 }
829 if let TypeRef::Map(_k, v) = ty {
830 if matches!(v.as_ref(), TypeRef::Json) {
831 if optional {
832 return format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())");
833 }
834 return format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v)).collect()");
835 }
836 }
837 }
838 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
840 if optional {
841 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
842 }
843 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
844 }
845 if !config.cast_large_ints_to_i64
846 && !config.cast_large_ints_to_f64
847 && !config.cast_uints_to_i32
848 && !config.cast_f32_to_f64
849 && !config.json_to_string
850 && !config.vec_named_to_string
851 && !config.map_as_string
852 && config.from_binding_skip_types.is_empty()
853 {
854 return field_conversion_to_core(name, ty, optional);
855 }
856 match ty {
858 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
859 let core_ty = core_prim_str(p);
860 if optional {
861 format!("{name}: val.{name}.map(|v| v as {core_ty})")
862 } else {
863 format!("{name}: val.{name} as {core_ty}")
864 }
865 }
866 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
868 if optional {
869 format!("{name}: val.{name}.map(|v| v as f32)")
870 } else {
871 format!("{name}: val.{name} as f32")
872 }
873 }
874 TypeRef::Duration if config.cast_large_ints_to_i64 => {
875 if optional {
876 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
877 } else {
878 format!("{name}: std::time::Duration::from_millis(val.{name} as u64)")
879 }
880 }
881 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
882 if let TypeRef::Primitive(p) = inner.as_ref() {
883 let core_ty = core_prim_str(p);
884 format!("{name}: val.{name}.map(|v| v as {core_ty})")
885 } else {
886 field_conversion_to_core(name, ty, optional)
887 }
888 }
889 TypeRef::Vec(inner)
891 if config.cast_large_ints_to_i64
892 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
893 {
894 if let TypeRef::Primitive(p) = inner.as_ref() {
895 let core_ty = core_prim_str(p);
896 if optional {
897 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
898 } else {
899 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
900 }
901 } else {
902 field_conversion_to_core(name, ty, optional)
903 }
904 }
905 TypeRef::Map(_k, v)
907 if config.cast_large_ints_to_i64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
908 {
909 if let TypeRef::Primitive(p) = v.as_ref() {
910 let core_ty = core_prim_str(p);
911 if optional {
912 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
913 } else {
914 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
915 }
916 } else {
917 field_conversion_to_core(name, ty, optional)
918 }
919 }
920 TypeRef::Vec(inner)
922 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
923 {
924 if optional {
925 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
926 } else {
927 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
928 }
929 }
930 TypeRef::Optional(inner)
932 if config.cast_f32_to_f64
933 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
934 {
935 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
936 }
937 TypeRef::Primitive(p) if config.cast_uints_to_i32 && needs_i32_cast(p) => {
939 let core_ty = core_prim_str(p);
940 if optional {
941 format!("{name}: val.{name}.map(|v| v as {core_ty})")
942 } else {
943 format!("{name}: val.{name} as {core_ty}")
944 }
945 }
946 TypeRef::Optional(inner)
948 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
949 {
950 if let TypeRef::Primitive(p) = inner.as_ref() {
951 let core_ty = core_prim_str(p);
952 format!("{name}: val.{name}.map(|v| v as {core_ty})")
953 } else {
954 field_conversion_to_core(name, ty, optional)
955 }
956 }
957 TypeRef::Vec(inner)
959 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
960 {
961 if let TypeRef::Primitive(p) = inner.as_ref() {
962 let core_ty = core_prim_str(p);
963 if optional {
964 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
965 } else {
966 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
967 }
968 } else {
969 field_conversion_to_core(name, ty, optional)
970 }
971 }
972 TypeRef::Primitive(p) if config.cast_large_ints_to_f64 && needs_f64_cast(p) => {
974 let core_ty = core_prim_str(p);
975 if optional {
976 format!("{name}: val.{name}.map(|v| v as {core_ty})")
977 } else {
978 format!("{name}: val.{name} as {core_ty}")
979 }
980 }
981 TypeRef::Optional(inner)
983 if config.cast_large_ints_to_f64
984 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
985 {
986 if let TypeRef::Primitive(p) = inner.as_ref() {
987 let core_ty = core_prim_str(p);
988 format!("{name}: val.{name}.map(|v| v as {core_ty})")
989 } else {
990 field_conversion_to_core(name, ty, optional)
991 }
992 }
993 TypeRef::Vec(inner)
995 if config.cast_large_ints_to_f64
996 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
997 {
998 if let TypeRef::Primitive(p) = inner.as_ref() {
999 let core_ty = core_prim_str(p);
1000 if optional {
1001 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1002 } else {
1003 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
1004 }
1005 } else {
1006 field_conversion_to_core(name, ty, optional)
1007 }
1008 }
1009 TypeRef::Map(_k, v)
1011 if config.cast_large_ints_to_f64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
1012 {
1013 if let TypeRef::Primitive(p) = v.as_ref() {
1014 let core_ty = core_prim_str(p);
1015 if optional {
1016 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
1017 } else {
1018 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
1019 }
1020 } else {
1021 field_conversion_to_core(name, ty, optional)
1022 }
1023 }
1024 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
1027 format!("{name}: Default::default()")
1028 }
1029 TypeRef::Optional(inner) => match inner.as_ref() {
1030 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
1031 format!("{name}: Default::default()")
1032 }
1033 _ => field_conversion_to_core(name, ty, optional),
1034 },
1035 _ => field_conversion_to_core(name, ty, optional),
1037 }
1038}
1039
1040pub fn apply_core_wrapper_to_core(
1043 conversion: &str,
1044 name: &str,
1045 core_wrapper: &CoreWrapper,
1046 vec_inner_core_wrapper: &CoreWrapper,
1047 optional: bool,
1048) -> String {
1049 if *vec_inner_core_wrapper == CoreWrapper::Arc {
1051 return conversion
1052 .replace(
1053 ".map(Into::into).collect()",
1054 ".map(|v| std::sync::Arc::new(v.into())).collect()",
1055 )
1056 .replace(
1057 "map(|v| v.into_iter().map(Into::into)",
1058 "map(|v| v.into_iter().map(|v| std::sync::Arc::new(v.into()))",
1059 );
1060 }
1061
1062 match core_wrapper {
1063 CoreWrapper::None => conversion.to_string(),
1064 CoreWrapper::Cow => {
1065 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1069 if optional {
1070 format!("{name}: {expr}.map(Into::into)")
1071 } else if expr == format!("val.{name}") {
1072 format!("{name}: val.{name}.into()")
1073 } else if expr == "Default::default()" {
1074 conversion.to_string()
1077 } else {
1078 format!("{name}: ({expr}).into()")
1079 }
1080 } else {
1081 conversion.to_string()
1082 }
1083 }
1084 CoreWrapper::Arc => {
1085 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1087 if expr == "Default::default()" {
1088 conversion.to_string()
1091 } else if optional {
1092 format!("{name}: {expr}.map(|v| std::sync::Arc::new(v))")
1093 } else {
1094 format!("{name}: std::sync::Arc::new({expr})")
1095 }
1096 } else {
1097 conversion.to_string()
1098 }
1099 }
1100 CoreWrapper::Bytes => {
1101 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1107 let already_converted_non_opt =
1108 expr == format!("val.{name}.into()") || expr == format!("val.{name}.to_vec().into()");
1109 let already_converted_opt = expr
1110 .strip_prefix(&format!("val.{name}"))
1111 .map(|s| s == ".map(Into::into)" || s == ".map(|v| v.to_vec().into())")
1112 .unwrap_or(false);
1113 if already_converted_non_opt || already_converted_opt {
1114 conversion.to_string()
1116 } else if optional {
1117 format!("{name}: {expr}.map(Into::into)")
1118 } else if expr == format!("val.{name}") {
1119 format!("{name}: val.{name}.into()")
1120 } else if expr == "Default::default()" {
1121 conversion.to_string()
1124 } else {
1125 format!("{name}: ({expr}).into()")
1126 }
1127 } else {
1128 conversion.to_string()
1129 }
1130 }
1131 CoreWrapper::ArcMutex => {
1132 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1134 if optional {
1135 format!("{name}: {expr}.map(|v| std::sync::Arc::new(std::sync::Mutex::new(v.into())))")
1136 } else if expr == format!("val.{name}") {
1137 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(val.{name}.into()))")
1138 } else {
1139 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(({expr}).into()))")
1140 }
1141 } else {
1142 conversion.to_string()
1143 }
1144 }
1145 }
1146}
1147
1148#[cfg(test)]
1149mod tests {
1150 use super::gen_from_binding_to_core;
1151 use super::gen_from_binding_to_core_cfg;
1152 use crate::conversions::ConversionConfig;
1153 use ahash::AHashSet;
1154 use alef_core::ir::{CoreWrapper, DefaultValue, FieldDef, TypeDef, TypeRef};
1155
1156 fn type_with_field(field: FieldDef) -> TypeDef {
1157 TypeDef {
1158 name: "ProcessConfig".to_string(),
1159 rust_path: "crate::ProcessConfig".to_string(),
1160 original_rust_path: String::new(),
1161 fields: vec![field],
1162 methods: vec![],
1163 is_opaque: false,
1164 is_clone: true,
1165 is_copy: false,
1166 doc: String::new(),
1167 cfg: None,
1168 is_trait: false,
1169 has_default: true,
1170 has_stripped_cfg_fields: false,
1171 is_return_type: false,
1172 serde_rename_all: None,
1173 has_serde: true,
1174 super_traits: vec![],
1175 binding_excluded: false,
1176 binding_exclusion_reason: None,
1177 }
1178 }
1179
1180 #[test]
1181 fn sanitized_cow_string_field_converts_to_core() {
1182 let field = FieldDef {
1183 name: "language".to_string(),
1184 ty: TypeRef::String,
1185 optional: false,
1186 default: None,
1187 doc: String::new(),
1188 sanitized: true,
1189 is_boxed: false,
1190 type_rust_path: None,
1191 cfg: None,
1192 typed_default: Some(DefaultValue::Empty),
1193 core_wrapper: CoreWrapper::Cow,
1194 vec_inner_core_wrapper: CoreWrapper::None,
1195 newtype_wrapper: None,
1196 serde_rename: None,
1197 serde_flatten: false,
1198 binding_excluded: false,
1199 binding_exclusion_reason: None,
1200 };
1201
1202 let out = gen_from_binding_to_core(&type_with_field(field), "crate");
1203
1204 assert!(out.contains("language: val.language.into()"));
1205 assert!(!out.contains("language: Default::default()"));
1206 }
1207
1208 #[test]
1212 fn trait_bridge_arc_wrapper_field_forwards_value_not_default() {
1213 let opaque_type_name = "VisitorHandle".to_string();
1214 let mut opaque_set = AHashSet::new();
1215 opaque_set.insert(opaque_type_name.clone());
1216
1217 let field = FieldDef {
1218 name: "visitor".to_string(),
1219 ty: TypeRef::Named(opaque_type_name.clone()),
1220 optional: true,
1221 default: None,
1222 doc: String::new(),
1223 sanitized: false,
1224 is_boxed: false,
1225 type_rust_path: None,
1226 cfg: Some("feature = \"visitor\"".to_string()),
1227 typed_default: None,
1228 core_wrapper: CoreWrapper::None,
1229 vec_inner_core_wrapper: CoreWrapper::None,
1230 newtype_wrapper: None,
1231 serde_rename: None,
1232 serde_flatten: false,
1233 binding_excluded: false,
1234 binding_exclusion_reason: None,
1235 };
1236
1237 let never_skip = vec!["visitor".to_string()];
1238 let arc_wrapper = vec!["visitor".to_string()];
1239
1240 let config = ConversionConfig {
1241 opaque_types: Some(&opaque_set),
1242 never_skip_cfg_field_names: &never_skip,
1243 trait_bridge_arc_wrapper_field_names: &arc_wrapper,
1244 ..ConversionConfig::default()
1245 };
1246
1247 let out = gen_from_binding_to_core_cfg(&type_with_field(field), "crate", &config);
1248
1249 assert!(
1250 out.contains("val.visitor.map(|v| (*v.inner).clone())"),
1251 "expected arc-wrapper clone forwarding, got:\n{out}"
1252 );
1253 assert!(
1254 !out.contains("visitor: Default::default()"),
1255 "must not emit Default::default() for arc-wrapper trait-bridge field, got:\n{out}"
1256 );
1257 }
1258
1259 #[test]
1262 fn opaque_no_wrapper_field_without_arc_flag_emits_default() {
1263 let opaque_type_name = "OpaqueHandle".to_string();
1264 let mut opaque_set = AHashSet::new();
1265 opaque_set.insert(opaque_type_name.clone());
1266
1267 let field = FieldDef {
1268 name: "handle".to_string(),
1269 ty: TypeRef::Named(opaque_type_name.clone()),
1270 optional: false,
1271 default: None,
1272 doc: String::new(),
1273 sanitized: false,
1274 is_boxed: false,
1275 type_rust_path: None,
1276 cfg: None,
1277 typed_default: None,
1278 core_wrapper: CoreWrapper::None,
1279 vec_inner_core_wrapper: CoreWrapper::None,
1280 newtype_wrapper: None,
1281 serde_rename: None,
1282 serde_flatten: false,
1283 binding_excluded: false,
1284 binding_exclusion_reason: None,
1285 };
1286
1287 let config = ConversionConfig {
1288 opaque_types: Some(&opaque_set),
1289 ..ConversionConfig::default()
1291 };
1292
1293 let out = gen_from_binding_to_core_cfg(&type_with_field(field), "crate", &config);
1294
1295 assert!(
1296 out.contains("handle: Default::default()"),
1297 "expected Default::default() for non-arc-wrapper opaque field, got:\n{out}"
1298 );
1299 assert!(
1300 !out.contains("(*val.handle.inner).clone()"),
1301 "must not emit arc-clone for non-arc-wrapper opaque field, got:\n{out}"
1302 );
1303 }
1304}