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 if field.cfg.is_some()
143 && !config.never_skip_cfg_field_names.contains(&field.name)
144 && (typ.has_stripped_cfg_fields || config.strip_cfg_fields_from_binding_struct)
145 {
146 continue;
147 }
148 fields.push(format!("{}: Default::default()", field.name));
149 continue;
150 }
151 let references_excluded = !config.exclude_types.is_empty()
163 && super::helpers::field_references_excluded_type(&field.ty, config.exclude_types);
164 if references_excluded && typ.has_stripped_cfg_fields {
165 continue;
166 }
167 if field.cfg.is_some()
173 && !config.never_skip_cfg_field_names.contains(&field.name)
174 && config.strip_cfg_fields_from_binding_struct
175 {
176 continue;
177 }
178 if optionalized && ((field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded) {
179 continue;
180 }
181 let field_was_optionalized = optionalized && !field.optional;
182 let conversion = if (field.sanitized && field.core_wrapper != CoreWrapper::Cow) || references_excluded {
183 format!("{}: Default::default()", field.name)
184 } else if field_was_optionalized {
185 field_conversion_to_core_cfg(&field.name, &field.ty, false, config)
188 } else {
189 field_conversion_to_core_cfg(&field.name, &field.ty, field.optional, config)
190 };
191 let conversion = if let Some(newtype_path) = &field.newtype_wrapper {
197 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
198 match &field.ty {
201 TypeRef::Optional(_) => format!("{}: ({expr}).map({newtype_path})", field.name),
202 TypeRef::Vec(_) => {
203 let inner_expr = if let Some(prefix) = expr.strip_suffix(".collect()") {
208 format!("{prefix}.collect::<Vec<_>>()")
209 } else {
210 expr.to_string()
211 };
212 format!(
213 "{}: ({inner_expr}).into_iter().map({newtype_path}).collect()",
214 field.name
215 )
216 }
217 _ if field.optional => format!("{}: ({expr}).map({newtype_path})", field.name),
218 _ => format!("{}: {newtype_path}({expr})", field.name),
219 }
220 } else {
221 conversion
222 }
223 } else {
224 conversion
225 };
226 let conversion = if field.is_boxed && matches!(&field.ty, TypeRef::Named(_)) {
228 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
229 if field.optional {
230 format!("{}: {}.map(Box::new)", field.name, expr)
232 } else {
233 format!("{}: Box::new({})", field.name, expr)
234 }
235 } else {
236 conversion
237 }
238 } else {
239 conversion
240 };
241 let is_opaque_arc_field = field.core_wrapper == CoreWrapper::Arc
249 && matches!(&field.ty, TypeRef::Named(n) if config
250 .opaque_types
251 .is_some_and(|opaque| opaque.contains(n.as_str())));
252 let is_opaque_no_wrapper_field = field.core_wrapper == CoreWrapper::None
256 && matches!(&field.ty, TypeRef::Named(n) if config
257 .opaque_types
258 .is_some_and(|opaque| opaque.contains(n.as_str())));
259 let conversion = if is_opaque_arc_field {
260 if field.optional {
261 format!("{}: val.{}.map(|v| v.inner)", field.name, field.name)
262 } else {
263 format!("{}: val.{}.inner", field.name, field.name)
264 }
265 } else if is_opaque_no_wrapper_field {
266 if config.trait_bridge_field_is_arc_wrapper(&field.name) {
270 if field.optional {
271 format!("{}: val.{}.map(|v| (*v.inner).clone())", field.name, field.name)
272 } else {
273 format!("{}: (*val.{}.inner).clone()", field.name, field.name)
274 }
275 } else {
276 format!("{}: Default::default()", field.name)
277 }
278 } else {
279 apply_core_wrapper_to_core(
280 &conversion,
281 &field.name,
282 &field.core_wrapper,
283 &field.vec_inner_core_wrapper,
284 field.optional,
285 )
286 };
287 let binding_name_field = config.binding_field_name_owned(&typ.name, &field.name);
291 let conversion = if binding_name_field != field.name {
292 conversion.replace(&format!("val.{}", field.name), &format!("val.{binding_name_field}"))
293 } else {
294 conversion
295 };
296 if optionalized {
297 if let Some(expr) = conversion.strip_prefix(&format!("{}: ", field.name)) {
298 if field_was_optionalized {
299 statements.push(format!(
300 "if let Some(__v) = val.{binding_name_field} {{ __result.{} = {}; }}",
301 field.name,
302 expr.replace(&format!("val.{binding_name_field}"), "__v")
303 ));
304 } else {
305 statements.push(format!("__result.{} = {};", field.name, expr));
306 }
307 }
308 } else {
309 fields.push(conversion);
310 }
311 }
312
313 crate::template_env::render(
317 "conversions/binding_to_core_impl",
318 minijinja::context! {
319 core_path => core_path,
320 binding_name => binding_name,
321 is_newtype => false,
322 newtype_inner_expr => "",
323 builder_mode => optionalized,
324 uses_builder_pattern => uses_builder_pattern,
325 has_stripped_cfg_fields => typ.has_stripped_cfg_fields,
326 statements => statements,
327 fields => fields,
328 },
329 )
330}
331
332pub(super) fn gen_optionalized_field_to_core(
337 name: &str,
338 ty: &TypeRef,
339 config: &ConversionConfig,
340 field_is_ir_optional: bool,
341) -> String {
342 match ty {
343 TypeRef::Json if config.json_as_value => {
344 format!("{name}: val.{name}.unwrap_or_default()")
345 }
346 TypeRef::Json => {
347 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok()).unwrap_or_default()")
348 }
349 TypeRef::Named(_) => {
350 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
352 }
353 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
354 format!("{name}: val.{name}.map(|v| v as f32).unwrap_or(0.0)")
355 }
356 TypeRef::Primitive(PrimitiveType::F32 | PrimitiveType::F64) => {
357 format!("{name}: val.{name}.unwrap_or(0.0)")
358 }
359 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
360 let core_ty = core_prim_str(p);
361 format!("{name}: val.{name}.map(|v| v as {core_ty}).unwrap_or_default()")
362 }
363 TypeRef::Optional(inner)
364 if config.cast_large_ints_to_i64
365 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
366 {
367 if let TypeRef::Primitive(p) = inner.as_ref() {
368 let core_ty = core_prim_str(p);
369 format!("{name}: val.{name}.map(|v| v as {core_ty})")
370 } else {
371 field_conversion_to_core(name, ty, false)
372 }
373 }
374 TypeRef::Duration if config.cast_large_ints_to_i64 => {
375 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64)).unwrap_or_default()")
376 }
377 TypeRef::Duration => {
378 format!("{name}: val.{name}.map(std::time::Duration::from_millis).unwrap_or_default()")
379 }
380 TypeRef::Path => {
381 format!("{name}: val.{name}.map(Into::into).unwrap_or_default()")
382 }
383 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Path) => {
384 format!("{name}: val.{name}.map(|s| std::path::PathBuf::from(s))")
386 }
387 TypeRef::Optional(_) => {
388 format!("{name}: val.{name}.map(Some)")
391 }
392 TypeRef::Char => {
394 format!("{name}: val.{name}.and_then(|s| s.chars().next()).unwrap_or('*')")
395 }
396 TypeRef::Vec(inner) => match inner.as_ref() {
397 TypeRef::Json => {
398 format!(
399 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()).unwrap_or_default()"
400 )
401 }
402 TypeRef::Named(_) => {
403 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect()).unwrap_or_default()")
404 }
405 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
406 let core_ty = core_prim_str(p);
407 format!(
408 "{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect()).unwrap_or_default()"
409 )
410 }
411 _ => format!("{name}: val.{name}.unwrap_or_default()"),
412 },
413 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
414 let k_is_json = matches!(k.as_ref(), TypeRef::Json);
418 let k_expr = if k_is_json {
419 "serde_json::from_str(&k).unwrap_or_default()"
420 } else {
421 "k.into()"
422 };
423 format!(
424 "{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()"
425 )
426 }
427 TypeRef::Map(k, _v) if matches!(k.as_ref(), TypeRef::Json) => {
428 format!(
430 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (serde_json::from_str(&k).unwrap_or_default(), v)).collect()"
431 )
432 }
433 TypeRef::Map(k, v) => {
434 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
436 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
437 let val_is_string_enum = matches!(v.as_ref(), TypeRef::Named(n)
438 if config.enum_string_names.as_ref().is_some_and(|names| names.contains(n)));
439 if field_is_ir_optional {
440 if val_is_string_enum {
442 format!(
443 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect())"
444 )
445 } else if has_named_val {
446 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
447 } else if has_named_key {
448 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())")
449 } else {
450 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
451 }
452 } else if val_is_string_enum {
453 format!(
454 "{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, serde_json::from_str(&v).unwrap_or_default())).collect()"
455 )
456 } else if has_named_val {
457 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k, v.into())).collect()")
458 } else if has_named_key {
459 format!("{name}: val.{name}.unwrap_or_default().into_iter().map(|(k, v)| (k.into(), v)).collect()")
460 } else {
461 format!("{name}: val.{name}.unwrap_or_default().into_iter().collect()")
462 }
463 }
464 _ => {
465 format!("{name}: val.{name}.unwrap_or_default()")
467 }
468 }
469}
470
471pub fn field_conversion_to_core(name: &str, ty: &TypeRef, optional: bool) -> String {
473 match ty {
474 TypeRef::Primitive(_) | TypeRef::String | TypeRef::Unit => {
476 format!("{name}: val.{name}")
477 }
478 TypeRef::Bytes => {
483 if optional {
484 format!("{name}: val.{name}.map(|v| v.to_vec().into())")
485 } else {
486 format!("{name}: val.{name}.to_vec().into()")
487 }
488 }
489 TypeRef::Json => {
491 if optional {
492 format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())")
493 } else {
494 format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()")
495 }
496 }
497 TypeRef::Char => {
499 if optional {
500 format!("{name}: val.{name}.and_then(|s| s.chars().next())")
501 } else {
502 format!("{name}: val.{name}.chars().next().unwrap_or('*')")
503 }
504 }
505 TypeRef::Duration => {
507 if optional {
508 format!("{name}: val.{name}.map(std::time::Duration::from_millis)")
509 } else {
510 format!("{name}: std::time::Duration::from_millis(val.{name})")
511 }
512 }
513 TypeRef::Path => {
515 if optional {
516 format!("{name}: val.{name}.map(Into::into)")
517 } else {
518 format!("{name}: val.{name}.into()")
519 }
520 }
521 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
524 format!("{name}: val.{name}")
525 }
526 TypeRef::Named(_) => {
527 if optional {
528 format!("{name}: val.{name}.map(Into::into)")
529 } else {
530 format!("{name}: val.{name}.into()")
531 }
532 }
533 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
537 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
538 "serde_json::from_str(&k).unwrap_or_default()"
539 } else {
540 "k.into()"
541 };
542 if optional {
543 format!(
544 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
545 )
546 } else {
547 format!(
548 "{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect()"
549 )
550 }
551 }
552 TypeRef::Map(_k, v) if matches!(v.as_ref(), TypeRef::Bytes) => {
555 if optional {
556 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.to_vec().into())).collect())")
557 } else {
558 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v.to_vec().into())).collect()")
559 }
560 }
561 TypeRef::Optional(inner) => match inner.as_ref() {
563 TypeRef::Json => format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())"),
564 TypeRef::Named(_) | TypeRef::Path => format!("{name}: val.{name}.map(Into::into)"),
565 TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Named(_)) => {
566 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
567 }
568 TypeRef::Map(k, v) if matches!(v.as_ref(), TypeRef::Json) => {
569 let k_expr = if matches!(k.as_ref(), TypeRef::Json) {
570 "serde_json::from_str(&k).unwrap_or_default()"
571 } else {
572 "k.into()"
573 };
574 format!(
575 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, serde_json::from_str(&v).unwrap_or_default())).collect())"
576 )
577 }
578 TypeRef::Vec(_) => {
581 format!("{name}: val.{name}.map(|v| v.into_iter().collect())")
582 }
583 _ => format!("{name}: val.{name}"),
584 },
585 TypeRef::Vec(inner) => match inner.as_ref() {
587 TypeRef::Json => {
588 if optional {
589 format!(
590 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect())"
591 )
592 } else {
593 format!("{name}: val.{name}.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()")
594 }
595 }
596 TypeRef::Named(type_name) if is_tuple_type_name(type_name) => {
598 format!("{name}: val.{name}")
599 }
600 TypeRef::Named(_) => {
601 if optional {
602 format!("{name}: val.{name}.map(|v| v.into_iter().map(Into::into).collect())")
603 } else {
604 format!("{name}: val.{name}.into_iter().map(Into::into).collect()")
605 }
606 }
607 _ => {
612 if optional {
613 format!("{name}: val.{name}.map(|v| v.into_iter().collect())")
614 } else {
615 format!("{name}: val.{name}.into_iter().collect()")
616 }
617 }
618 },
619 TypeRef::Map(k, v) => {
622 let has_named_key = matches!(k.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
623 let has_named_val = matches!(v.as_ref(), TypeRef::Named(n) if !is_tuple_type_name(n));
624 let has_json_val = matches!(v.as_ref(), TypeRef::Json);
625 let has_json_key = matches!(k.as_ref(), TypeRef::Json);
626 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)));
628 let has_vec_json_val = matches!(v.as_ref(), TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
630 if has_json_val || has_json_key || has_named_key || has_named_val || has_vec_named_val || has_vec_json_val {
631 let k_expr = if has_json_key {
635 "serde_json::from_str(&k).unwrap_or(serde_json::Value::String(k))"
636 } else {
637 "k.into()"
638 };
639 let v_expr = if has_json_val {
640 "serde_json::from_str(&v).unwrap_or(serde_json::Value::String(v))"
641 } else if has_named_val {
642 "v.into()"
643 } else if has_vec_named_val {
644 "v.into_iter().map(Into::into).collect()"
645 } else if has_vec_json_val {
646 "v.into_iter().filter_map(|s| serde_json::from_str(&s).ok()).collect()"
647 } else {
648 "v"
649 };
650 if optional {
651 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect())")
652 } else {
653 format!("{name}: val.{name}.into_iter().map(|(k, v)| ({k_expr}, {v_expr})).collect()")
654 }
655 } else {
656 let is_string_map = matches!(k.as_ref(), TypeRef::String) && matches!(v.as_ref(), TypeRef::String);
660 if is_string_map {
661 if optional {
662 format!(
663 "{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v.into())).collect())"
664 )
665 } else {
666 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v.into())).collect()")
667 }
668 } else {
669 if optional {
673 if has_named_val {
674 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v.into())).collect())")
675 } else {
676 format!("{name}: val.{name}.map(|m| m.into_iter().collect())")
677 }
678 } else {
679 format!("{name}: val.{name}.into_iter().collect()")
680 }
681 }
682 }
683 }
684 }
685}
686
687pub fn field_conversion_to_core_cfg(name: &str, ty: &TypeRef, optional: bool, config: &ConversionConfig) -> String {
689 if optional && matches!(ty, TypeRef::Optional(_)) {
693 let inner_expr = field_conversion_to_core_cfg(name, ty, false, config);
696 if let Some(expr) = inner_expr.strip_prefix(&format!("{name}: ")) {
698 return format!("{name}: ({expr}).map(Some)");
699 }
700 return inner_expr;
701 }
702
703 if config.map_uses_jsvalue {
705 let is_nested_vec = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Vec(_)));
706 let is_vec_json = matches!(ty, TypeRef::Vec(inner) if matches!(inner.as_ref(), TypeRef::Json));
707 let is_map = matches!(ty, TypeRef::Map(_, _));
708 if is_nested_vec || is_map || is_vec_json {
709 if optional {
710 return format!(
711 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
712 );
713 }
714 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
715 }
716 if let TypeRef::Optional(inner) = ty {
717 let is_inner_nested = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Vec(_)));
718 let is_inner_vec_json = matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Json));
719 let is_inner_map = matches!(inner.as_ref(), TypeRef::Map(_, _));
720 if is_inner_nested || is_inner_map || is_inner_vec_json {
721 return format!(
722 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
723 );
724 }
725 }
726 }
727
728 if config.vec_named_to_string {
732 if let TypeRef::Vec(inner) = ty {
733 if matches!(inner.as_ref(), TypeRef::Named(_)) {
734 if optional {
735 return format!("{name}: val.{name}.as_ref().and_then(|s| serde_json::from_str(s).ok())");
736 }
737 return format!("{name}: serde_json::from_str(&val.{name}).unwrap_or_default()");
738 }
739 }
740 }
741 if config.map_as_string && matches!(ty, TypeRef::Map(_, _)) {
743 return format!("{name}: Default::default()");
744 }
745 if config.map_as_string {
746 if let TypeRef::Optional(inner) = ty {
747 if matches!(inner.as_ref(), TypeRef::Map(_, _)) {
748 return format!("{name}: Default::default()");
749 }
750 }
751 }
752 if config.map_uses_jsvalue {
757 if let Some(tagged_names) = config.tagged_data_enum_names {
758 let bare_named = matches!(ty, TypeRef::Named(n) if tagged_names.contains(n));
759 let optional_named = matches!(ty, TypeRef::Optional(inner)
760 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n)));
761 let vec_named = matches!(ty, TypeRef::Vec(inner)
762 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n)));
763 let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
764 if matches!(outer.as_ref(), TypeRef::Vec(inner)
765 if matches!(inner.as_ref(), TypeRef::Named(n) if tagged_names.contains(n))));
766 if bare_named {
767 if optional {
768 return format!(
771 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
772 );
773 }
774 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
776 }
777 if optional_named {
778 return format!(
780 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
781 );
782 }
783 if vec_named {
784 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
785 }
786 if optional_vec_named {
787 return format!(
788 "{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())"
789 );
790 }
791 }
792 }
793
794 if let Some(untagged_names) = config.untagged_data_enum_names {
797 let direct_named = matches!(ty, TypeRef::Named(n) if untagged_names.contains(n));
798 let optional_named = matches!(ty, TypeRef::Optional(inner)
799 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
800 let vec_named = matches!(ty, TypeRef::Vec(inner)
801 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n)));
802 let optional_vec_named = matches!(ty, TypeRef::Optional(outer)
803 if matches!(outer.as_ref(), TypeRef::Vec(inner)
804 if matches!(inner.as_ref(), TypeRef::Named(n) if untagged_names.contains(n))));
805 if direct_named {
806 if optional {
807 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
808 }
809 return format!("{name}: serde_json::from_value(val.{name}).unwrap_or_default()");
810 }
811 if optional_named {
812 return format!("{name}: val.{name}.and_then(|v| serde_json::from_value(v).ok())");
813 }
814 if vec_named {
815 if optional {
816 return format!(
817 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
818 );
819 }
820 return format!("{name}: val.{name}.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect()");
821 }
822 if optional_vec_named {
823 return format!(
824 "{name}: val.{name}.map(|v| v.into_iter().filter_map(|x| serde_json::from_value(x).ok()).collect())"
825 );
826 }
827 }
828 if config.json_to_string && matches!(ty, TypeRef::Json) {
830 return format!("{name}: Default::default()");
831 }
832 if config.json_as_value && matches!(ty, TypeRef::Json) {
834 return format!("{name}: val.{name}");
835 }
836 if config.json_as_value {
837 if let TypeRef::Optional(inner) = ty {
838 if matches!(inner.as_ref(), TypeRef::Json) {
839 return format!("{name}: val.{name}");
840 }
841 }
842 if let TypeRef::Vec(inner) = ty {
843 if matches!(inner.as_ref(), TypeRef::Json) {
844 if optional {
845 return format!("{name}: val.{name}.unwrap_or_default()");
846 }
847 return format!("{name}: val.{name}");
848 }
849 }
850 if let TypeRef::Map(_k, v) = ty {
851 if matches!(v.as_ref(), TypeRef::Json) {
852 if optional {
853 return format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k.into(), v)).collect())");
854 }
855 return format!("{name}: val.{name}.into_iter().map(|(k, v)| (k.into(), v)).collect()");
856 }
857 }
858 }
859 if config.map_uses_jsvalue && matches!(ty, TypeRef::Json) {
861 if optional {
862 return format!("{name}: val.{name}.as_ref().and_then(|v| serde_wasm_bindgen::from_value(v.clone()).ok())");
863 }
864 return format!("{name}: serde_wasm_bindgen::from_value(val.{name}.clone()).unwrap_or_default()");
865 }
866 if !config.cast_large_ints_to_i64
867 && !config.cast_large_ints_to_f64
868 && !config.cast_uints_to_i32
869 && !config.cast_f32_to_f64
870 && !config.json_to_string
871 && !config.vec_named_to_string
872 && !config.map_as_string
873 && config.from_binding_skip_types.is_empty()
874 {
875 return field_conversion_to_core(name, ty, optional);
876 }
877 match ty {
879 TypeRef::Primitive(p) if config.cast_large_ints_to_i64 && needs_i64_cast(p) => {
880 let core_ty = core_prim_str(p);
881 if optional {
882 format!("{name}: val.{name}.map(|v| v as {core_ty})")
883 } else {
884 format!("{name}: val.{name} as {core_ty}")
885 }
886 }
887 TypeRef::Primitive(PrimitiveType::F32) if config.cast_f32_to_f64 => {
889 if optional {
890 format!("{name}: val.{name}.map(|v| v as f32)")
891 } else {
892 format!("{name}: val.{name} as f32")
893 }
894 }
895 TypeRef::Duration if config.cast_large_ints_to_i64 => {
896 if optional {
897 format!("{name}: val.{name}.map(|v| std::time::Duration::from_millis(v as u64))")
898 } else {
899 format!("{name}: std::time::Duration::from_millis(val.{name} as u64)")
900 }
901 }
902 TypeRef::Optional(inner) if matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) => {
903 if let TypeRef::Primitive(p) = inner.as_ref() {
904 let core_ty = core_prim_str(p);
905 format!("{name}: val.{name}.map(|v| v as {core_ty})")
906 } else {
907 field_conversion_to_core(name, ty, optional)
908 }
909 }
910 TypeRef::Vec(inner)
912 if config.cast_large_ints_to_i64
913 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
914 {
915 if let TypeRef::Primitive(p) = inner.as_ref() {
916 let core_ty = core_prim_str(p);
917 if optional {
918 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
919 } else {
920 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
921 }
922 } else {
923 field_conversion_to_core(name, ty, optional)
924 }
925 }
926 TypeRef::Map(_k, v)
928 if config.cast_large_ints_to_i64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_i64_cast(p)) =>
929 {
930 if let TypeRef::Primitive(p) = v.as_ref() {
931 let core_ty = core_prim_str(p);
932 if optional {
933 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
934 } else {
935 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
936 }
937 } else {
938 field_conversion_to_core(name, ty, optional)
939 }
940 }
941 TypeRef::Vec(inner)
943 if config.cast_f32_to_f64 && matches!(inner.as_ref(), TypeRef::Primitive(PrimitiveType::F32)) =>
944 {
945 if optional {
946 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
947 } else {
948 format!("{name}: val.{name}.into_iter().map(|v| v as f32).collect()")
949 }
950 }
951 TypeRef::Optional(inner)
953 if config.cast_f32_to_f64
954 && matches!(inner.as_ref(), TypeRef::Vec(vi) if matches!(vi.as_ref(), TypeRef::Primitive(PrimitiveType::F32))) =>
955 {
956 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as f32).collect())")
957 }
958 TypeRef::Primitive(p) if config.cast_uints_to_i32 && needs_i32_cast(p) => {
960 let core_ty = core_prim_str(p);
961 if optional {
962 format!("{name}: val.{name}.map(|v| v as {core_ty})")
963 } else {
964 format!("{name}: val.{name} as {core_ty}")
965 }
966 }
967 TypeRef::Optional(inner)
969 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
970 {
971 if let TypeRef::Primitive(p) = inner.as_ref() {
972 let core_ty = core_prim_str(p);
973 format!("{name}: val.{name}.map(|v| v as {core_ty})")
974 } else {
975 field_conversion_to_core(name, ty, optional)
976 }
977 }
978 TypeRef::Vec(inner)
980 if config.cast_uints_to_i32 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_i32_cast(p)) =>
981 {
982 if let TypeRef::Primitive(p) = inner.as_ref() {
983 let core_ty = core_prim_str(p);
984 if optional {
985 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
986 } else {
987 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
988 }
989 } else {
990 field_conversion_to_core(name, ty, optional)
991 }
992 }
993 TypeRef::Primitive(p) if config.cast_large_ints_to_f64 && needs_f64_cast(p) => {
995 let core_ty = core_prim_str(p);
996 if optional {
997 format!("{name}: val.{name}.map(|v| v as {core_ty})")
998 } else {
999 format!("{name}: val.{name} as {core_ty}")
1000 }
1001 }
1002 TypeRef::Optional(inner)
1004 if config.cast_large_ints_to_f64
1005 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
1006 {
1007 if let TypeRef::Primitive(p) = inner.as_ref() {
1008 let core_ty = core_prim_str(p);
1009 format!("{name}: val.{name}.map(|v| v as {core_ty})")
1010 } else {
1011 field_conversion_to_core(name, ty, optional)
1012 }
1013 }
1014 TypeRef::Vec(inner)
1016 if config.cast_large_ints_to_f64
1017 && matches!(inner.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
1018 {
1019 if let TypeRef::Primitive(p) = inner.as_ref() {
1020 let core_ty = core_prim_str(p);
1021 if optional {
1022 format!("{name}: val.{name}.map(|v| v.into_iter().map(|x| x as {core_ty}).collect())")
1023 } else {
1024 format!("{name}: val.{name}.into_iter().map(|v| v as {core_ty}).collect()")
1025 }
1026 } else {
1027 field_conversion_to_core(name, ty, optional)
1028 }
1029 }
1030 TypeRef::Map(_k, v)
1032 if config.cast_large_ints_to_f64 && matches!(v.as_ref(), TypeRef::Primitive(p) if needs_f64_cast(p)) =>
1033 {
1034 if let TypeRef::Primitive(p) = v.as_ref() {
1035 let core_ty = core_prim_str(p);
1036 if optional {
1037 format!("{name}: val.{name}.map(|m| m.into_iter().map(|(k, v)| (k, v as {core_ty})).collect())")
1038 } else {
1039 format!("{name}: val.{name}.into_iter().map(|(k, v)| (k, v as {core_ty})).collect()")
1040 }
1041 } else {
1042 field_conversion_to_core(name, ty, optional)
1043 }
1044 }
1045 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
1048 format!("{name}: Default::default()")
1049 }
1050 TypeRef::Optional(inner) => match inner.as_ref() {
1051 TypeRef::Named(n) if config.from_binding_skip_types.iter().any(|s| s == n) => {
1052 format!("{name}: Default::default()")
1053 }
1054 _ => field_conversion_to_core(name, ty, optional),
1055 },
1056 _ => field_conversion_to_core(name, ty, optional),
1058 }
1059}
1060
1061pub fn apply_core_wrapper_to_core(
1064 conversion: &str,
1065 name: &str,
1066 core_wrapper: &CoreWrapper,
1067 vec_inner_core_wrapper: &CoreWrapper,
1068 optional: bool,
1069) -> String {
1070 if *vec_inner_core_wrapper == CoreWrapper::Arc {
1072 return conversion
1073 .replace(
1074 ".map(Into::into).collect()",
1075 ".map(|v| std::sync::Arc::new(v.into())).collect()",
1076 )
1077 .replace(
1078 "map(|v| v.into_iter().map(Into::into)",
1079 "map(|v| v.into_iter().map(|v| std::sync::Arc::new(v.into()))",
1080 );
1081 }
1082
1083 match core_wrapper {
1084 CoreWrapper::None => conversion.to_string(),
1085 CoreWrapper::Cow => {
1086 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1090 if optional {
1091 format!("{name}: {expr}.map(Into::into)")
1092 } else if expr == format!("val.{name}") {
1093 format!("{name}: val.{name}.into()")
1094 } else if expr == "Default::default()" {
1095 conversion.to_string()
1098 } else {
1099 format!("{name}: ({expr}).into()")
1100 }
1101 } else {
1102 conversion.to_string()
1103 }
1104 }
1105 CoreWrapper::Arc => {
1106 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1108 if expr == "Default::default()" {
1109 conversion.to_string()
1112 } else if optional {
1113 format!("{name}: {expr}.map(|v| std::sync::Arc::new(v))")
1114 } else {
1115 format!("{name}: std::sync::Arc::new({expr})")
1116 }
1117 } else {
1118 conversion.to_string()
1119 }
1120 }
1121 CoreWrapper::Bytes => {
1122 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1128 let already_converted_non_opt =
1129 expr == format!("val.{name}.into()") || expr == format!("val.{name}.to_vec().into()");
1130 let already_converted_opt = expr
1131 .strip_prefix(&format!("val.{name}"))
1132 .map(|s| s == ".map(Into::into)" || s == ".map(|v| v.to_vec().into())")
1133 .unwrap_or(false);
1134 if already_converted_non_opt || already_converted_opt {
1135 conversion.to_string()
1137 } else if optional {
1138 format!("{name}: {expr}.map(Into::into)")
1139 } else if expr == format!("val.{name}") {
1140 format!("{name}: val.{name}.into()")
1141 } else if expr == "Default::default()" {
1142 conversion.to_string()
1145 } else {
1146 format!("{name}: ({expr}).into()")
1147 }
1148 } else {
1149 conversion.to_string()
1150 }
1151 }
1152 CoreWrapper::ArcMutex => {
1153 if let Some(expr) = conversion.strip_prefix(&format!("{name}: ")) {
1155 if optional {
1156 format!("{name}: {expr}.map(|v| std::sync::Arc::new(std::sync::Mutex::new(v.into())))")
1157 } else if expr == format!("val.{name}") {
1158 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(val.{name}.into()))")
1159 } else {
1160 format!("{name}: std::sync::Arc::new(std::sync::Mutex::new(({expr}).into()))")
1161 }
1162 } else {
1163 conversion.to_string()
1164 }
1165 }
1166 }
1167}
1168
1169#[cfg(test)]
1170mod tests {
1171 use super::gen_from_binding_to_core;
1172 use super::gen_from_binding_to_core_cfg;
1173 use crate::conversions::ConversionConfig;
1174 use ahash::AHashSet;
1175 use alef_core::ir::{CoreWrapper, DefaultValue, FieldDef, TypeDef, TypeRef};
1176
1177 fn type_with_field(field: FieldDef) -> TypeDef {
1178 TypeDef {
1179 name: "ProcessConfig".to_string(),
1180 rust_path: "crate::ProcessConfig".to_string(),
1181 original_rust_path: String::new(),
1182 fields: vec![field],
1183 methods: vec![],
1184 is_opaque: false,
1185 is_clone: true,
1186 is_copy: false,
1187 doc: String::new(),
1188 cfg: None,
1189 is_trait: false,
1190 has_default: true,
1191 has_stripped_cfg_fields: false,
1192 is_return_type: false,
1193 serde_rename_all: None,
1194 has_serde: true,
1195 super_traits: vec![],
1196 binding_excluded: false,
1197 binding_exclusion_reason: None,
1198 }
1199 }
1200
1201 #[test]
1202 fn sanitized_cow_string_field_converts_to_core() {
1203 let field = FieldDef {
1204 name: "language".to_string(),
1205 ty: TypeRef::String,
1206 optional: false,
1207 default: None,
1208 doc: String::new(),
1209 sanitized: true,
1210 is_boxed: false,
1211 type_rust_path: None,
1212 cfg: None,
1213 typed_default: Some(DefaultValue::Empty),
1214 core_wrapper: CoreWrapper::Cow,
1215 vec_inner_core_wrapper: CoreWrapper::None,
1216 newtype_wrapper: None,
1217 serde_rename: None,
1218 serde_flatten: false,
1219 binding_excluded: false,
1220 binding_exclusion_reason: None,
1221 original_type: None,
1222 };
1223
1224 let out = gen_from_binding_to_core(&type_with_field(field), "crate");
1225
1226 assert!(out.contains("language: val.language.into()"));
1227 assert!(!out.contains("language: Default::default()"));
1228 }
1229
1230 #[test]
1231 fn binding_excluded_cfg_field_is_not_emitted_into_core_literal() {
1232 let field = FieldDef {
1233 name: "di_container".to_string(),
1234 ty: TypeRef::String,
1235 optional: true,
1236 default: None,
1237 doc: String::new(),
1238 sanitized: false,
1239 is_boxed: false,
1240 type_rust_path: None,
1241 cfg: Some("feature = \"di\"".to_string()),
1242 typed_default: None,
1243 core_wrapper: CoreWrapper::None,
1244 vec_inner_core_wrapper: CoreWrapper::None,
1245 newtype_wrapper: None,
1246 serde_rename: None,
1247 serde_flatten: false,
1248 binding_excluded: true,
1249 binding_exclusion_reason: Some("internal implementation detail".to_string()),
1250 original_type: None,
1251 };
1252 let mut typ = type_with_field(field);
1253 typ.has_stripped_cfg_fields = true;
1254
1255 let out = gen_from_binding_to_core(&typ, "crate");
1256
1257 assert!(
1258 !out.contains("di_container:"),
1259 "cfg-gated binding-excluded fields may not exist in the core struct; got:\n{out}"
1260 );
1261 assert!(
1262 out.contains("..Default::default()"),
1263 "stripped cfg fields should be filled by the default update; got:\n{out}"
1264 );
1265 }
1266
1267 #[test]
1271 fn trait_bridge_arc_wrapper_field_forwards_value_not_default() {
1272 let opaque_type_name = "VisitorHandle".to_string();
1273 let mut opaque_set = AHashSet::new();
1274 opaque_set.insert(opaque_type_name.clone());
1275
1276 let field = FieldDef {
1277 name: "visitor".to_string(),
1278 ty: TypeRef::Named(opaque_type_name.clone()),
1279 optional: true,
1280 default: None,
1281 doc: String::new(),
1282 sanitized: false,
1283 is_boxed: false,
1284 type_rust_path: None,
1285 cfg: Some("feature = \"visitor\"".to_string()),
1286 typed_default: None,
1287 core_wrapper: CoreWrapper::None,
1288 vec_inner_core_wrapper: CoreWrapper::None,
1289 newtype_wrapper: None,
1290 serde_rename: None,
1291 serde_flatten: false,
1292 binding_excluded: false,
1293 binding_exclusion_reason: None,
1294 original_type: None,
1295 };
1296
1297 let never_skip = vec!["visitor".to_string()];
1298 let arc_wrapper = vec!["visitor".to_string()];
1299
1300 let config = ConversionConfig {
1301 opaque_types: Some(&opaque_set),
1302 never_skip_cfg_field_names: &never_skip,
1303 trait_bridge_arc_wrapper_field_names: &arc_wrapper,
1304 ..ConversionConfig::default()
1305 };
1306
1307 let out = gen_from_binding_to_core_cfg(&type_with_field(field), "crate", &config);
1308
1309 assert!(
1310 out.contains("val.visitor.map(|v| (*v.inner).clone())"),
1311 "expected arc-wrapper clone forwarding, got:\n{out}"
1312 );
1313 assert!(
1314 !out.contains("visitor: Default::default()"),
1315 "must not emit Default::default() for arc-wrapper trait-bridge field, got:\n{out}"
1316 );
1317 }
1318
1319 #[test]
1322 fn opaque_no_wrapper_field_without_arc_flag_emits_default() {
1323 let opaque_type_name = "OpaqueHandle".to_string();
1324 let mut opaque_set = AHashSet::new();
1325 opaque_set.insert(opaque_type_name.clone());
1326
1327 let field = FieldDef {
1328 name: "handle".to_string(),
1329 ty: TypeRef::Named(opaque_type_name.clone()),
1330 optional: false,
1331 default: None,
1332 doc: String::new(),
1333 sanitized: false,
1334 is_boxed: false,
1335 type_rust_path: None,
1336 cfg: None,
1337 typed_default: None,
1338 core_wrapper: CoreWrapper::None,
1339 vec_inner_core_wrapper: CoreWrapper::None,
1340 newtype_wrapper: None,
1341 serde_rename: None,
1342 serde_flatten: false,
1343 binding_excluded: false,
1344 binding_exclusion_reason: None,
1345 original_type: None,
1346 };
1347
1348 let config = ConversionConfig {
1349 opaque_types: Some(&opaque_set),
1350 ..ConversionConfig::default()
1352 };
1353
1354 let out = gen_from_binding_to_core_cfg(&type_with_field(field), "crate", &config);
1355
1356 assert!(
1357 out.contains("handle: Default::default()"),
1358 "expected Default::default() for non-arc-wrapper opaque field, got:\n{out}"
1359 );
1360 assert!(
1361 !out.contains("(*val.handle.inner).clone()"),
1362 "must not emit arc-clone for non-arc-wrapper opaque field, got:\n{out}"
1363 );
1364 }
1365}